8.30vue响应式原理

  1. 编译模块
    效果//姓名:{{name}},年龄:{{age}},居住:{{addr.province}}
    //返回 姓名:mon,年龄:17,居住:dd
    //obj = { 姓名:mon,年龄:17,addr:{
              province:'等等',
              city:'ddd'
            },}
    //只有嵌套对象没有数组
    function compile(template,obj) {
     let res = gettemplate(template) //把模版字符串中需要替换的{{}}找出来
     for(let i = 0; i < res.length; i++) {
       template = replace (template,res[i],obj) //然后把{{}}替换对应的属性值
     }
     return template
    }
    function gettemplate(template) { return template ? template.match(/{{[^}]+}}/g) : [] // {{}}数组 }
    function replace (template,subTemplate,obj){ let prop = subTemplate.replace('{{','').replace('}}','') //根据{{}}找出对应的属性 let value = getValue(obj,prop)//根据属性得到属性值 return template.replace(subTemplate,value) //把{{}}替换成属性值 }
    function getValue(obj,prop) { let props = prop.split('.')//嵌套对象的属性数组 let value = obj for(let i = 0; i < props.length; i++) { value = value[props[i]] } return value }

     

  2. 创建虚拟节点
    function node(realDom,template) { //真实节点,模版字符串===节点文本
     this.realDom = realDom
     this.template = template
     this.children = [] //子节点数组
    }
    
    function createNode(realDom){
     let template = ''  
     if(realDom.nodeType === Node.TEXT_NODE) {//判断是否是文本节点
     template = realDom.nodeValue 
     } 
     let vnode = node(realDom,template)
     for (let i = 0; i < realDom.childNodes.length; i++) { //子节点
       let childnode = realDom.childNodes[i] 
       let childVnode  = createNode(childnode)
       node.children.push(childVnode)
     }
     return node
    }

    虚拟节点
    //
    node {realDom: div#app, template: "", children: Array(5)}
      1. children: (5) [node, node, node, node, node]
      2. realDom: div#app
      3. template: ""
      4. __proto__: Object
    
    

     

  3. 渲染页面的文本
    function render (node,obj) {
     //找到包含{{}}的文本节点
     node.template = node.template.trim()
     if(node.template) {
     let newText = compile( node.template,obj) //如果有{{}}就得到编译后的字符串
      if(newText !==node.realDom.nodeValue){//如果有更新就让节点更新
      node.realDom.nodeValue  = newText
      }
     }else {
      for (let i = 0; i < node.children.length; i++) { //没有{{}}就递归渲染子节点
          render(node.children[i],obj)
        } 
     }
    }

    //
    <p>姓名:a</p>
    <p>年龄:17</p>
    <p>居住:等等</p>
    
    

     

  4. 响应式
    //Object.defineProperty
    //将原始对象obj的所有属性复制到targetobj,并且让targetobj属性都具有响应式,当改变时就运行回调函数
    
    function createResponse((obj,targetobj,callback) {
     for(const key in obj) {
     clone(obj,key,targetobj,callback)
     }
    }
    
    function clone(obj,key,targetobj,callbac){ //克隆对象并且有每个属性有响应式
     if(typeof obj[prop] === 'object') {//如果属性是对象
      let newObj = {}//把属性对象又用新的对象这样克隆并且拥有响应式
      createResponse((obj[prop],newObj,callback) 
      Object.defineProperty(target,prop,{
        get:function (params) {
            return newobj
          },
        set:function (params) {
            obj[prop] = params //响应式
            if (typeof params === 'object') { //如果修改的属性值是对象 又需要创建新的对象来让修改的属性拥有响应式
              newobj = {}
              createResponse(params,newobj,callback)
            }else {
              newobj = params //不是对象就直接赋值
            }
            callback && callback()
          }
      })
     }else {       //属性不是对象就直接defineProperty
     Object.defineProperty(target,prop,{
       get:function(){
         return obj[prop] //返回原来的属性
       },
       set:function(val){
        obj[prop] = params //响应式
        callback && callback()
       }
      })
     }
    }
    //

    vm.age = 10
    10

    <p>年龄:10</p>

    
    

     

  5. 合并
    export default function Vue(options) { //绑定实例
      let el = options.el
      this.$data = options.data
      this.$el = document.querySelector(el) 
      
      this.$node = createNode( this.$el) //虚拟节点
       render(this.$node,this.$data) //渲染文本
       createResponse(this.$data,this,() => { //响应式
         render(this.$node,this.$data)
       })
    
    }

     

  6. html
     <div id="app">
        <p>姓名:{{name}}</p>
        <p>年龄:{{age}}</p>
    <p>居住:{{addr.province}}</p>
    </div>
      <button onclick='vm.age++'>+</button> 
    
    
     let obj = {
            name:'moc',
            age:17,
            addr:{
              province:'等等',
              city:'ddd'
            },
            
          }
          let newobj = {}
          createResponse(obj,newobj,() => {
            console.log('有属性改变了')
          })
          window.newobj = newobj
    
       window.vm = new Vue({
        el:'#app',
        data:{
          name:'a',
          age:17,
          addr:{
              province:'等等',
              city:'ddd'
            },
        }
      }) 

     

posted @ 2020-08-30 11:08  尽世间恶丑  阅读(178)  评论(0编辑  收藏  举报