Vue 生命周期

一、概念

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

下图展示了实例的生命周期。

二、执行过程

1、beforeCreate / created

创建 Vue 实例时,会先执行 initLifecycle 方法注册生命周期的函数,再去执行 beforeCreate 钩子函数,此时还访问不到数据,

当执行 initState 方法初始化 propsdatamethodswatchcomputed 等属性后,调用 created 钩子函数,就可以去访问数据进行数据的初始化了。但是还不能操作 DOM。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// ..
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
// ..
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}

2、beforeMount / mounted

调用 vm.$mount 后,先将模版编译成渲染函数,然后执行 beforeMount 钩子函数,此时也不能操作 DOM。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
// ..
}
}
callHook(vm, 'beforeMount')

let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
// ..
vm._update(vnode, hydrating)
// ..
}
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}

// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
hydrating = false

// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}

当调用 updateComponent 回调函数后,会先将渲染函数转成 VNode,然后在将 VNode 渲染成 DOM 挂到页面中,挂载完成后,再去执行 mounted 钩子函数,此时 DOM 已经挂载,可以执行操作 DOM 的逻辑。

1
2
3
4
5
6
7
8
9
10
11
const componentVNodeHooks = {
// ..
insert (vnode: MountedComponentVNode) {
const { context, componentInstance } = vnode
if (!componentInstance._isMounted) {
componentInstance._isMounted = true
callHook(componentInstance, 'mounted')
}
// ..
}
}

3、beforeUpdate / updated

DOM 挂载完成之后,当数据发生变化时会先执行 beforeUpdate 方法 。此时改变的数据还没有更新到试图;数据的修改会通过响应式原理通知到每个观察者去执行回调函数更新 DOM,当 DOM 重新渲染完成之后数据就更新到了视图,此时再去执行 updated 钩子函数。

4、beforeDestory / destoryed

组件销毁前先执行 beforeDestory 钩子函数 ,此时实例仍然可用。然后开始执行一系列清理工作,清理完数据及监听的事件之后,在执行 destoryed 钩子函数。

5、activated / deactivated

activated 和 deactivated 钩子函数在使用 keep-alive 组件时出现。activated 在 keep-alive 缓存的组件激活时调用。deactivated 在 keep-alive 缓存的组件停用时调用。

三、思考

1、父组件和子组件生命周期执行顺序

  • 组件初始化渲染
1
2
3
4
5
父: beforeCreate => created => beforeMount => 

子: beforeCreate => created => beforeMount => mounted =>

父: mounted
  • 子组件更新
1
2
3
4
5
父: beforeUpdate => 

子: beforeUpdate => updated =>

父: updated
  • 父组件更新
1
父: beforeUpdate => updated
  • 组件销毁
1
2
3
4
5
父: beforeDestroy => 

子: beforeDestroy => destroyed =>

父: destroyed
微信打赏