Title:vue2 生命周期详解
在探究vue生命周期之前,我们先提出一个问题:
new Vue()
之后,发生了什么?,当数据(data
)发生变化之后又发生了什么?
生命周期流程图
由上图可知,new Vue()
之后,分别经过了以上几个阶段,分别是初始化阶段,模板编译阶段,挂载阶段,更新阶段,销毁阶段,那每一个阶段都干了些什么事呢?
初始化阶段
- 首先做一些初始化操作,主要是设置一些私有属性到
vue
实例中。 - 运行生命周期钩子函数
beforeCreate
- 进入注入流程,处理属性,
computed
,methods
,data
,provide
,inject
,最后使用代理模式将这些属性挂载到实例中。 - 运行生命周期钩子函数
created
编译阶段
- 生成
render
函数:如果有配置,直接使用配置的render
函数,如果没有,使用运行时编译器,把模板编译成render
函数。
挂载阶段
- 运行生命周期钩子函数
beforeMount
创建一个
Watcher
,传入一个函数updeteCompontent
,该函数会运行render
,函数,并把render
函数的返回结果vnode
作为参数给_updete
函数执行。// 伪代码 updateCompontent(){ _update(_render()) } new Watcher(updateCompontent)
在执行render
函数的过程中会搜集所有依赖,将来依赖发生变换时会出现执行updateCompontent
函数。
在执行_update
的过程中,会触发patch
函数,由于目前还没有就的虚拟DOM树,因此直接为当前的虚拟DOM树的每一个节点生成对应elm属性,即真实DOM。
如果遇到创建一个组件实例的vnode
,则会进入组件实例化流程,该流程同vue
实例流程,同上初始化阶段,编译阶段,挂载阶段。最终会把创建好的组件实例挂载到vnode
的compontentInstance
属性中,以便复用。
- 运行生命周期钩子函数mounted
更新阶段
- 数据发生变化后,所有依赖该数据的
watcher
都会重新运行。 watcher
会被调度器Scheduler
放到nextTick
中运行,参考vue数据响应式原理,也就是微队列中,这样避免避免多个依赖的数据同时改变后被多次执行。- 运行生命周期钩子函数beforeUpdate。
updateCompontent
函数重新执行:// 伪代码 updateCompontent(){ _update(_render()) } new Watcher(updateCompontent)
在执行
render
函数的过程中,会先去掉之前的依赖,重新收集新的依赖,将来依赖发生变化时出现运行updateCompontent
函数。在执行
update
函数的过程中,会触发patch
函数,对比新旧两棵DOM树:当对比两棵DOM树的节点的时候,有两种情况,分别:
普通
html
节点普通
html
节点的对比会导致真实节点被创建,删除,移动,更新组件节点
组件节点的对比会导致组件被创建,删除,移动,更新。
a)组件节点创建的时,进入组件实例化流程,同上初始化阶段,编译阶段,挂载阶段。
b)当旧组件节点删除时,会调用旧组件的
$destroy
方法删除组件,该方法会触发生命周期钩子函数beforeDestroy,然后递归调用组件的$destroy
方法,然后出发生命周期钩子函数destroyedc)当组件更新时,相当于组件的
updateCompontent
函数被重新触发,进入渲染流程,同更新阶段
- 运行生命周期钩子函数updated
销毁阶段
- 当组件销毁的时候,会调用组件的
$destroy
方法删除组件,该方法会调用beforeDestroy
和destroyed
方法
Vue.prototype.$destroy = function () {
// 该属性标志着当前实例是否处于正在被销毁的状态
if (vm._isBeingDestroyed) {
return
}
callHook(vm, \'beforeDestroy\')
vm._isBeingDestroyed = true
// remove self from parent
const parent = vm.$parent
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm)
}
// ...
callHook(vm, \'destroyed\')
// ...
vm.$off()
}
执行
beforeDestroy
方法后,将当前组件实例从父组件实例的$children
中删除if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { remove(parent.$children, vm) }
移除自身的依赖监听和事件监听,实例内响应式数据的引用
// teardown watchers if (vm._watcher) { vm._watcher.teardown() } let i = vm._watchers.length while (i--) { vm._watchers[i].teardown() } if (vm._data.__ob__) { vm._data.__ob__.vmCount-- } vm._isDestroyed = true vm.__patch__(vm._vnode, null)
- 执行
destroyed
方法后,通过vm.$off()
方法移除实例上的所有事件监听器,
扩展
回到开头那个问题,new Vue()
之后,发生了什么?,当数据(data
)发生变化之后又发生了什么?销毁呢?
// main.js
import Vue from \"vue\";
import App from \"./App.vue\";
Vue.config.productionTip = false;
new Vue({
render: (h) => h(App),
beforeCreate() {
console.log(\"vue实例 beforeCreate\");
},
created() {
console.log(\"vue实例 created\");
},
beforeMount() {
console.log(\"vue实例 beforeMount\");
},
mounted() {
console.log(\"vue实例 mounted\", this);
},
beforeUpdate() {
console.log(\"vue实例 beforeUpdate\");
},
updated() {
console.log(\"vue实例 updated\");
},
beforeDestroy() {
console.log(\"vue实例 beforeDestroy\");
},
destroyed() {
console.log(\"vue实例 destroyed\");
},
}).$mount(\"#app\");
// App
// A
A compnent: {{ count }}
// B
B compnent: {{ count }}
当new Vue()
后,会递归创建vue实例和组件实例:初始化阶段,模板编译阶段,挂载阶段。
当所有子组件实例创建完成后vue实例才创建完成
vue实例 beforeCreate
vue实例 created
vue实例 beforeMount
App beforeCreate
App created
App beforeMount
A beforeCreate
A created
A beforeMount
B beforeCreate
B created
B beforeMount
B mounted
A mounted
App mounted
vue实例 mounted
当数据变化时
App beforeUpdate
A beforeUpdate
B beforeUpdate
B updated
A updated
App updated
当组件A,B销毁时
App beforeUpdate
A beforeDestroy
B beforeDestroy
B destroyed
A destroyed
App updated
以上仅个人理解,如有不当之处还请不吝赐教