之前也写个一个vue2生命周期的解读,这次重新根据这本书再回忆一遍,加深印象。
(1)最开始new Vue()被调用,在new Vue()中会执行init()方法开始vue的初始化,如图:
(2)init的函数中就包括执行了一系列函数,如图:
包括vue实例创建完毕之后的挂载:
(3)然后调用mount之后就会到mountComponent函数,beforeMount,mounted,beforeUpdate钩子都在mountComponent函数里面,主要代码如下:
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
//一些警告和判断
if (!vm.$options.render) {
}
//执行beforeMount钩子函数
callHook(vm, 'beforeMount')
let updateComponent
//重新渲染组件的回调函数
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
//用Watcher监听vm,如果状态改变 则执行updateComponent回调
new Watcher(vm, updateComponent, noop, {
//执行回调之前执行beforeUpdate回调
before () {
callHook(vm, 'beforeUpdate')
}
}, true)
//渲染完毕执行mounted钩子函数
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
其实就整个生命周期来说,从源码来看,我觉得就是一系列的初始化函数以及钩子。
所以我们从上到下来看:
一、initLifecycle
源码就不放了,其实就是初始化一些属性和默认值,比如说它的parent(找到它的第一个非抽象类型的父级),它的children,它的root。
二、initEvents
这里初始化的事件,其实是父组件在模板中使用v-on监听子组件内触发(emit)的事件。
所有注册的事件的都保存在vm._events中,核心函数为updateComponentListeners()
三、initRender
initRender主要为vue渲染做初始化准备,也初始化了一些属性,最重要的是将creatElement()函数挂载到vue的_c上,为之后使用createElement创建虚拟DOM做准备
之后就触发钩子函数beforeCreate
四、initInjections // resolve injections before data/props
内部通过resolveInject来初始化inject
什么是inject:和provide一起使用,用于祖先向子孙注入一个依赖。祖先组件通过provide提供依赖,子孙组件通过inject注入依赖。
提一点,看到标题上的注释了吗?这是源码中的,告诉我们inject要在data/props之前初始化,而provide要在data/props之后初始化,所有就是为什么要先initInjections、再initState、再initProvide。
五、initState
这个函数就比较复杂了,里面包含了很多东西的初始化,总体来说,包含initProps,initMethods,initData,initComputed,initWatch,其实这个顺序也有讲究的。先初始化props,就可以在data中使用props,先初始化props和data,这样就可以在watch中观察它们了。
六、initProvide // resolve provide after data/props
其实就是把vm.options里面的provide赋值给vm
vm._provided = vm.$options.provide
之后就调用了created的钩子函数了,就可以使用props,methods,data,computed,watch了,已经我项目中一般也是在这里调用api,因为这个时候data已经是响应式的了
七、vm.$mount
在调用完created钩子函数之后,就会执行下面代码
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
mount方法分两个版本,runtime only和runtime+compile,我们一般都是runtime+compile,所以在mount函数中会通过compileToFunctions函数对template进行编译,编译成render函数,然后完成编译之后就return到mountComponent里面去了。
八、mountComponent
函数内会进行一些判断,然后就直接调用beforeMount钩子函数。也就是在beforeMount之前其实就是将模板编译成了render
然后将vm放入到Watcher中,并且放入updateComponent作为回调函数,如果状态返回变化,在变化之前,就会执行beforeUpdate回调。如果是第一次渲染,这里肯定会执行render生成虚拟DOM,然后生成视图。
代码如下:
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
完成渲染或者更新之后就会判断是否渲染完成,如果完成就调用mounted的钩子函数。
代码如下:
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
到这里就creat、mount、update的相关生命周期就到这里结束了,主要就说这个几个,其他的就不详细讲了。
其他的还有:
activated:被 keep-alive 缓存的组件激活时调用。
deactivated:被 keep-alive 缓存的组件失活时调用。
beforeDestory:实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
errorCaptured:在捕获一个来自后代组件的错误时被调用。(vue2.5+新增)