Vue的整个实现流程

作者: 泡杯感冒灵 | 来源:发表于2020-08-04 10:40 被阅读0次

    第一步,解析模板成render函数

    • with的用法
    • 模板中所有信息都被render函数包含
    • 模板中用到的data中的属性,都变成JS变量
    • 模板中的v-model,v-for,v-on都变成了JS逻辑
    • render函数返回vnode
      //模板
      <div id="app">
        <div>
          <input type="text" v-model="title">
          <button @click="add">add</button>
        </div>
        <div>
          <ul>
            <li v-for="(item,index) in lists" :key="index">{{item}}</li>
          </ul>
        </div>
      </div>
    
    //render函数
    with (this) {
      return _c(
        'div',
        {
          attrs: { "id": "app" }
        },
        [
          _c(
            'div',
            [
              _c(
                'input',
                {
                  directives: [
                    {
                      name: "model",
                      rawName: "v-model",
                      value: (title),
                      expression: "title"
                    }],
                  attrs: { "type": "text" },
                  domProps: { "value": (title) },
                  on: {
                    "input": function ($event) {
                      if ($event.target.composing) return;
                      title = $event.target.value
                    }
                  }
                }
              ),
              _v(" "),  // 换行 创建一个空字符串的文本节点
              _c(
                'button',
                {
                  on: { "click": add }
                },
                [
                  _v("add")
                ]
              )
            ]
          ),
          _v(" "), // 换行 创建一个空字符串的文本节点
          _c(
            'div',
            [_c(
              'ul',
              _l(
                (lists), function (item, index) { return _c('li', { key: index }, [_v(_s(item))]) }
              )
            )
            ]
          )
        ]
      )
    }
    
    

    第二步,响应式开始监听

    • Object.defineProperty
    • 将data属性代理到vm上
    // vm是一个vue实例
    // var vm = new Vue({
    //   el:'#app',
    //   data:{
    //     name:'lisi',
    //     age: 20
    //   }
    // })
    
    // 我们可以模拟一下,vue是怎么监听name和age的变化的,以及name和age怎么代理到vm实例上的
    var vm = {}
    var data = {
      name: 'lisi',
      age:20
    }
    
    var key, value
    for (key in data) {
      // 此时key会命中一个闭包,我们需要一个立即执行函数把每个循环到的key保留下来
      (function (key) {
        Object.defineProperty(vm, key, {
          get: function () {
            console.log('get',data[key])  // 监听
            return data[key]
          },
          set: function (newVal) {
            console.log('set',newVal)    // 监听
            data[key] = newVal
          }
        })
      })(key)
    }
    

    第三步,首次渲染,显示页面,且绑定依赖

    • 初次渲染,执行updateComponent,执行vm._render
    • 执行render函数,会访问到data里的数据,也就是访问vm上的数据 (vm.list和vm.title)
    • 会被响应式的get方法监听到
    • 执行updateComponent,会走到vdom的patch方法
    • patch将vnode渲染成DOM,初次渲染完成
    vm._update(vnode){
        const prevVnode = vm._vnode
        vm._vnode = vnode
        if(!prevVnode){
            vm.$el = vm.__patch__(vm.$el,vnode)
        }else{
            vm.$el = vm.__patch__(prevVnode,vnode)
        }
    }
    
    function updateComponent(){
        // vm._render即上边说的 render函数,会返回一个vnode
        vm._update(vm._render())
    }
    
    这里有个问题,为什么要监听get呢?get获取之后,什么都做不了,直接监听set不行吗?比如title被修改了,然后set监听到,直接把页面渲染成最新修改的数据。看原因
    • data中有很多属性,有些被用到,有些可能不被用到。
    • 被用到的(也就是被访问)会触发get,不被用到的不会触发get
    • 未触发get的属性,set的时候我们无需关心。
    • 目的是避免不必要的重新渲染。

    比如,data有3个属性,这3个属性中,其中title和list都被模板用了,而aaa没有。如果我们不先监听get,而是直接监听set,那么无论哪个属性被修改了,都会被set监听到,从而引起重新渲染。
    也就是说,aaa属性,虽然没有用到,当它改变的时候,也会被set监听到,从而重新执行updateComponent,
    updateComponent里也会重新执行一遍patch,执行结果虽然没有什么变化,但是只要执行,就会消耗性能,所以我们要避免不必要的重新渲染
    所以,我们先监听get,来判断这个属性是否被模板用到,只有被用到的属性改变了,set监听才会去重新渲染页面。而没有走get的属性,set的时候,无需关心。

    data = {
      title:'',
      list:[],
      aaa:''
    }
    

    第四步,data属性变化,触发rerender

    • 修改属性,被响应式的set监听到
    • set中执行 updateComponent
    • updateComponent重新执行 vm._render
    • 生成的vnode和prevnode,通过patch进行对比
    • 渲染到html中
     var data = {
          title:'',
          lists:[]
        }
        var vm = new Vue({
            el:"#app",
            data:data,
            methods:{
                add:function(){
                    if(!this.title){
                        return 
                    }
                    this.lists.push(this.title)
                    this.title = ''
                }
            }
        })
    
    
    var key, value
    for (key in data) {
      // 此时key会命中一个闭包,我们需要一个立即执行函数把每个循环到的key保留下来
      (function (key) {
        Object.defineProperty(vm, key, {
          get: function () {
            console.log('get',data[key])  // 监听
            return data[key]
          },
          set: function (newVal) {
            console.log('set',newVal)    // 监听
            data[key] = newVal
          }
        })
      })(key)
    }
    

    相关文章

      网友评论

        本文标题:Vue的整个实现流程

        本文链接:https://www.haomeiwen.com/subject/nnvirktx.html