Vue2生命周期及组件实例创建流程

72 阅读2分钟

new Vue之后发生了什么?数据改变后,又发生了什么?

image-20240604163152965.png

function Vue(options){
    var data = options.data();
    observe(data); // 使用观察将数据变成响应式
    
    Object.defineProperty(this, "a", {
        get(){
            return data.a //将属性挂载到实例
        }
        set(val){
            this.a = val
        }
    })
    
    // 将methods中的方法绑定到this
    Object.entries(methods).forEach(([methodsName,fn])=>{
        this[methodsName] = fn.bind(this)
    })
​
    // 7.
    var updateComponent = () =>{
        this._update(this._render)
    }
    
    new Watcher(updateComponent)
    
}

创建vue实例和创建组件的流程基本一致

  1. 首先做一些初始化操作,主要是设置一些私有属性到实例中

  2. 运行生命周期钩子函数beforeCreate

  3. 进入注入流程:处理computed、methods、data、provide、inject,最后使用代理模式将他们挂载到实例中

  4. 运行生命周期函数created

  5. 生成render函数:如果有配置,直接使用配置的render,如果没有,使用运行时编译器,把模板编译为render

  6. 运行生命周期beforeMount

  7. 创建一个Watcher,传入一个函数updateComponent,该函数会运行render,把得到的VNode再传入_update函数执行

    在执行render函数的过程中,会收集所有依赖,将来依赖改变时会重新运行updateComponent函数

    在执行_update函数的过程中,触发patch函数,由于目前没有旧树,因此直接为当前虚拟DOM树的每一个普通节点生成elm属性,即真实DOM

    如果遇到创建一个组件的vnode,则会进入组件实例化流程,该流程和创建vue实例流程基本相同,最终会把创建好的组件实例挂载vnode的componentInstance属性中,以便复用

  8. 运行生命周期mounted

重渲染

  1. 数据变化后,所有依赖该数据的Watcher均会重新运行,这里仅考虑updateComponent函数对应的Watcher

  2. Watcher会被调度器放到nextTick中运行,也就是微队列中,这样为了避免多个依赖的数据同时改变后被多次执行

  3. 运行生命周期beforeUpdate

  4. updateComponent函数重新执行

    在执行render函数的过程中,会去掉之前的依赖,重新收集所有依赖,将来依赖变化时会重新运行updateComponent函数

    在执行_update函数的过程中,触发patch函数

    新旧两棵树进行对比

    普通html节点的对比会导致真实节点被创建、删除、移动、更新

    组件节点的对比会导致组件被创建、删除、移动、更新

    当新组件需要创建时,进入实例化流程

    当旧组件需要删除时,会调用就组件的$destroy方法删除组件,该方法会先触发生命周期beforeDestroy,然后递归调用子组件的$destroy方法,然后触发生命周期destroyed

    当组件属性更新时,相当于组件的updateComponent函数被重新触发执行,进入重渲染流程,和本节点相同

  5. 运行生命周期updated