共计 4831 个字符,预计需要花费 13 分钟才能阅读完成。
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
<template>
<div id="app">
<h1>App</h1>
<A v-if="show" :count="count" />
<button @click="count++">increase</button>
<button @click="show = !show">toggle</button>
</div>
</template>
<script>
import A from "./A.vue";
export default {components: { A},
data() {
return {
show: true,
count: 0,
};
},
beforeCreate() {console.log("App beforeCreate");
},
created() {console.log("App created");
},
beforeMount() {console.log("App beforeMount");
},
mounted() {console.log("App mounted");
},
beforeUpdate() {console.log("App beforeUpdate");
},
updated() {console.log("App updated");
},
beforeDestroy() {console.log("App beforeDestroy");
},
destroyed() {console.log("App destroyed");
},
};
</script>
// A
<template>
<div>
<h1>A compnent: {{count}}</h1>
<B :count="count" />
</div>
</template>
<script>
import B from "./B.vue";
export default {components: { B},
props: ["count"],
beforeCreate() {console.log("A beforeCreate");
},
created() {console.log("A created");
},
beforeMount() {console.log("A beforeMount");
},
mounted() {console.log("A mounted");
},
beforeUpdate() {console.log("A beforeUpdate");
},
updated() {console.log("A updated");
},
beforeDestroy() {console.log("A beforeDestroy");
},
destroyed() {console.log("A destroyed");
},
};
</script>
<style></style>
// B
<template>
<div>
<h1>B compnent: {{count}}</h1>
</div>
</template>
<script>
export default {props: ["count"],
beforeCreate() {console.log("B beforeCreate");
},
created() {console.log("B created");
},
beforeMount() {console.log("B beforeMount");
},
mounted() {console.log("B mounted");
},
beforeUpdate() {console.log("B beforeUpdate");
},
updated() {console.log("B updated");
},
beforeDestroy() {console.log("B beforeDestroy");
},
destroyed() {console.log("B destroyed");
},
};
</script>
<style></style>
当 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
以上仅集体了解,如有不当之处还请不吝赐教