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 watchersif (vm._watcher) { vm._watcher.teardown()}let i = vm._watchers.lengthwhile (i--) { vm._watchers[i].teardown()}if (vm._data.__ob__) { vm._data.__ob__.vmCount--}vm._isDestroyed = truevm.__patch__(vm._vnode, null)
- 执行
destroyed
办法后,通过vm.$off()
办法移除实例上的所有事件监听器,
扩大
回到结尾那个问题,new Vue()
之后,产生了什么?,当数据(data
)发生变化之后又产生了什么?销毁呢?
// main.jsimport 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 beforeCreateApp createdApp beforeMount A beforeCreate A created A beforeMount B beforeCreate B created B beforeMount B mounted A mountedApp mounted
vue实例 mounted
当数据变动时
App beforeUpdate
A beforeUpdate B beforeUpdate B updatedA updated
App updated
当组件A,B销毁时
App beforeUpdate
A beforeDestroy B beforeDestroy B destroyedA destroyed
App updated
以上仅集体了解,如有不当之处还请不吝赐教