Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
下面的表格展示了每个生命周期分别在什么时候被调用:
生命周期
描述
beforeCreate
在实例初始化之后,数据观测(data observer) 之前被调用。
created
实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。但真实 dom 还没有生成,$el 还不可用
beforeMount
在挂载开始之前被调用,相关的 render 函数首次被调用。
mounted
el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
activited
keep-alive 专属,组件被激活时调用
deactivated
keep-alive 专属,组件被销毁时调用
beforeDestory
实例销毁之前调用。在这一步,实例仍然完全可用。
destoryed
Vue 实例销毁后调用。
这里放上官网的生命周期流程图:
我这里用一张图梳理了源码中关于周期的全流程(长图预警):
Vue本质上是一个构造函数,定义在src/core/instance/index.js中:
// src/core/instance/index.js
function Vue(options) {
if (process.env.NODE_ENV !== "production" && !(this instanceof Vue)) {
warn("Vue is a constructor and should be called with the `new` keyword");
}
this._init(options);
}
回答这个问题,我们先要概括的回答一下
Vue生命周期
是什么:Vue
实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom
-> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue
的生命周期。下面的表格展示了每个生命周期分别在什么时候被调用:
beforeCreate
data observer
) 之前被调用。created
data observer
),属性和方法的运算,watch/event
事件回调。但真实dom
还没有生成,$el
还不可用beforeMount
render
函数首次被调用。mounted
el
被新创建的vm.$el
替换,并挂载到实例上去之后调用该钩子。beforeUpdate
DOM
重新渲染和打补丁之前。updated
DOM
重新渲染和打补丁,在这之后会调用该钩子。activited
keep-alive
专属,组件被激活时调用deactivated
keep-alive
专属,组件被销毁时调用beforeDestory
destoryed
Vue
实例销毁后调用。这里放上官网的生命周期流程图:
我这里用一张图梳理了源码中关于周期的全流程(长图预警):
Vue
本质上是一个构造函数,定义在src/core/instance/index.js
中:构造函数的核心是调用了
_init
方法,_init
定义在src/core/instance/init.js
中:_init
内调用了很多初始化函数,从函数名称可以看出分别是执行初始化生命周期(initLifecycle
)、初始化事件中心(initEvents
)、初始化渲染(initRender
)、执行beforeCreate
钩子(callHook(vm, 'beforeCreate')
)、解析inject(initInjections
)、初始化状态(initState
)、解析provide(initProvide
)、执行created
钩子(callHook(vm, 'created')
)。_init
函数的最后有判断如果有el
就执行$mount
方法。定义在src/platforms/web/entry-runtime-with-compiler.js
中:这里面主要做了两件事:
1、 重写了
Vue
函数的原型上的$mount
函数2、 判断是否有模板,并且将模板转化成
render
函数最后调用了
runtime
的mount
方法,用来挂载组件,也就是mountComponent
方法。mountComponent
内首先调用了beforeMount
方法,然后在初次渲染和更新后会执行vm._update(vm._render(), hydrating)
方法。最后渲染完成后调用mounted
钩子。beforeUpdate
和updated
钩子是在页面发生变化,触发更新后,被调用的,对应是在src/core/observer/scheduler.js
的flushSchedulerQueue
函数中。beforeDestroy
和destroyed
都在执行$destroy
函数时被调用。$destroy
函数是定义在Vue.prototype
上的一个方法,对应在src/core/instance/lifecycle.js
文件中:Vue.prototype.$destroy = function() { const vm: Component = this; 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); } // teardown watchers if (vm._watcher) { vm._watcher.teardown(); } let i = vm._watchers.length; while (i--) { vm._watchers[i].teardown(); } // remove reference from data ob // frozen object may not have observer. if (vm._data.ob) { vm._data.ob.vmCount--; } // call the last hook... vm._isDestroyed = true; // invoke destroy hooks on current rendered tree vm.patch(vm._vnode, null); // fire destroyed hook callHook(vm, "destroyed"); // turn off all instance listeners. vm.$off(); // remove vue reference if (vm.$el) { vm.$el.vue = null; } // release circular reference (#6759) if (vm.$vnode) { vm.$vnode.parent = null; } };