zoukankan      html  css  js  c++  java
  • 手撕VUE源码(一):手写一个MVVM

    class Vue{
      constructor(options){
        this.$el = options.el; //获取元素节点
        this.$data = options.data; //获取数据
        //若元素节点不为空
        if(this.$el){
    
          new Observer(this.$data); //数据劫持
    
          new Compiler(this); //解析节点变为虚拟dom,放到内存中
        }
      }
    }
    
    class Observer{
      constructor(data){
        this.observer (data);
      }
    
      observer(data){
        //判断是否为对象
        if(data && typeof data == 'object'){
          for(let key in data){
            this.reactive(key,data[key],data); //对key属性绑定数据劫持
          }
        }
      }
    
      reactive(key,value,data){
        
        this.observer(value); //对嵌套的子属性继续递归劫持
        
        Object.defineProperties(data,key,{
          get(){
            return value;
          },
          set: newValue => { //
            if(newValue != value){
              
              value = newValue;
              
              this.observer(newValue);//若新值为一个对象则对新对象再次劫持
            }
          }
        })
      }
    }
    
    class Compiler{
      constructor(vm){
        this.vm = vm;
        //判断el是元素节点还是元素ID
        this.el = this.isElementNode(vm.$el)?el:document.querySelector(el);
        
        let fregment = this.getChildNodes(this.el); //将子元素放入内存
        
        //将模板语法转换成绑定的数据
        this.compilerToData(fregment);
      }
    
      compilerToData(fregment){
        //开始转换
        [...fregment.childNodes].forEach(child =>{
          //判断子节点类型是元素还是文本节点
          if(isElementNode(child)){
            this.compilerElementNode(child); //转换元素节点
            this.compilerToData(child); //递归遍找到所有的孩子
          }else{
            this.compilerTextNode(child); //转换文本节点
          }
        })
      }
    
      compilerElementNode(node){
        let attributes = node.attributes; //节点的attributes属性也是个类数组
        [...attributes].forEach( attr => {
          let {name:vueDirect, value:express} = attr; //v-model="jsc" 对象结构,冒号后面代表将属性的值赋给新的变量名
          //判断属性是否为vue指令
          if(this.isVueDirect(vueDirect)){
            let [,vueDirect] = vueDirect.split("-") //对象结构,对“v-model”再做一次分割
            CompilerMapUtil[vueDirect](node,this.vm,express); //CompilerMapUtil是一个映射关系,不同的vue指令对应不同的解析处理方式
          }
        })
      }
    
      compilerTextNode(node){
        let textcontent = node.textcontent; //获取文本内容
        if(/{{(.+?)}}/.test(textcontent)) {
          CompilerMapUtil["text"](node,vm,textcontent)
        }
      }
      
      isVueDirect(name){
        if(name.startsWith("v-")){
          return true; 
        }
        return false;
      }
    
      getChildNodes(el){
        let fregment = document.createDocumentFragment();
        let firstChilNode;
        //每次取一个子节点放入内存中,直到取完为止
        while(firstChilNode = el.firstChild){
          fregment.appendChild(firstChilNode);
        }
        return fregment;
      }
    
      isElementNode(node){
        return node.nodeType ===1;
      }
    }
    
    //vue指令及解析处理关系映射
    CompilerMapUtil = {
    
      model(node,vueObject,express){
        
        //根据model属性的变量名字express,从vue对象vm中取数据(jsc:11111,express的值是jsc)
        let modelValue = this.getValue(vueObject,express);
        
        let fn = this.updater['modelUpdater'] //获取model指令对应的赋值方法
        
        fn(node,modelValue); //将vue对象中的值赋给输入框
      },
      text(node,vueObject,content){
        //正则表达式的g表示全局,意为找出所有{{}}进行替换
        //因为箭头函数没有arguments,因此使用...args剩余参数表示法,可以将类数组arguments转换成数组类型变量args接收
        let textContent = content.replace(/{{(.+?)}}/g,(...args)=>{
          //replace的回调函数可以设置5个参数,该函数的第一个参数是匹配模式的子串。第二个参数是与模式中子表达式匹配的子串
          //args第一个参数是正则表达式匹配的{{jsc}},第二个参数是子表达式匹配到的jsc
          return this.getValue(vueObject,args[1]);
        })
    
        let fn = this.updater['textUpdater'] // 获取文本节点对应的赋值方法
    
        fn(node,textContent); //进行赋值操作
      },
      getValue(vueObject,express){ 
        //express在这里是jsc.name(v-model="jsc.name")
        //reduce会遍历[jsc,name]这个数组,并把vue的数据对象作为第一次的dataObject参数
        //每一次的返回值作为下一次的dataObject入参
        express.split(".").reduce( (dataObject,currentKey) => {
          return dataObject[currentKey];
        },vueObject.$data)
      },
      updater:{
        modelUpdater(node,modelValue){
          node.value = modelValue;
        },
        textUpdater(node, modelValue){
          node.textcontent = modelValue
        }
      },
    
      for(){
    
      },
    
      if(){
    
      }
    }
  • 相关阅读:
    人脸识别总结(附开源项目代码与各大数据集下载路径)
    simpledet 的配置
    论文笔记--PCN:Real-Time Rotation-Invariant Face Detection with Progressive Calibration Networks
    smallcorgi/Faster-RCNN_TF训练自己的数据
    保存文件名至txt文件中,不含后缀
    训练 smallcorgi/Faster-RCNN_TF 模型(附ImageNet model百度云下载地址)
    调试 smallcorgi/Faster-RCNN_TF 的demo过程遇到的问题
    python字符串前缀和格式化
    摩斯电码与字母相互转换
    django配置mysql
  • 原文地址:https://www.cnblogs.com/jiangxiaoxi/p/12590671.html
Copyright © 2011-2022 走看看