pocket-manual
pocket-manual copied to clipboard
关于 Vue 实例的生命周期
每一个 Vue 实例都有一个完整的生命周期,其包括实例创建、设置数据监听、编译模板、将实例挂载到 DOM以及在数据变化时更新 DOM 等。
Vue 实例的生命周期简单概括就是从创建到挂载再到销毁,以及过程中的更新。
在整个生命周期过程中,Vue 提供了一些生命周期钩子,给了我们在不同阶段执行自定义逻辑的机会。
接下来总结一下生命周期的概念以及生命周期钩子的使用。
话不多说,先来看一个例子(JSFiddle):
<div id="app">
<p>{{ message }}</p>
</div>
new Vue({
el: "#app",
data() {
return {
message: 'Here goes lifecycle hooks demo'
}
},
beforeCreate() {
console.group('%cbeforeCreate:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
},
created() {
console.group('%ccreated:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
},
beforeMount() {
console.group('%cbeforeMount:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
},
mounted() {
console.group('%cmounted:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
this.message = 'The message has been changed' // 触发 update
},
beforeUpdate() {
console.group('%cbeforeUpdate:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
},
updated() {
console.group('%cupdated:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
this.$destroy() // 触发 destroy
},
beforeDestroy() {
console.group('%cbeforeDestroy:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
},
destroyed() {
console.group('%cdestroyed:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
// 实例销毁后
this.message = 'The message has been changed again' // 触发 update
console.group('%cafterDestroyed:', 'color:#3ab882')
console.log(this.$el && this.$el.innerHTML)
console.log(this.$data)
console.log(this.message)
console.groupEnd()
}
})
运行结果:

-
beforeCreate
从控制台的输出结果可以看到,
$el和$data在这个阶段并未初始化,均返回undefined。该钩子函数在实例初始化之后、data observer 和 event/watcher 事件配置之前被调用。
-
created
从控制台的输出结果可以看到,这个阶段完成了数据的初始化,所以
$data和message均可访问,但是$el未初始化,其返回undefined。该钩子函数在实例创建完成之后被调用,实例完成 data observer、属性和方法的运算以及 watch/event 事件回调。但是还未进入挂载阶段,
$el属性不可见,正是以上$el返回undefined的原因。一般我们会在这个阶段做一些数据请求操作。 -
beforeMount
从控制台的输出结果可以看到,这个阶段完成了
$el和$data的初始化,所以相关属性均可访问。该钩子函数在挂载之前被调用,相关
render函数首次被调用,Vue 实例完成了模板编译并结合数据生成 HTML,但是还未挂载到页面,所以此时this.$el.innerHTML输出为<p>{{ message }}</p>。 -
mounted
从控制台的输出结果可以看到,这个阶段完成了挂载。
该钩子函数在
el被新创建的vm.$el替换并挂载到实例上之后被调用,用编译好的 HTML 替换el指向的 DOM 对象并渲染到页面上。 -
beforeUpdate
代码中,我通过修改
message触发数据更新,beforeUpdate先被调用。该钩子函数在数据更新之前被调用,所以此时
this.$el.innerHTML输出为<p>Here goes lifecycle hooks demo</p>,其仍为修改前的数据。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。如果在该钩子函数中进一步修改状态,由于 Vue 异步更新有去重机制,不会触发附加的重渲染过程。
-
updated
从控制台的输出结果可以看到,这个阶段完成了数据更新。
该钩子函数在由于数据修改导致虚拟 DOM 重新渲染和
patch之后被调用,调用时组件 DOM 已经完成更新,所以一般可用于执行一些依赖于更新后 DOM 的操作。但是在大多数情况下,我们应该避免在此期间更改状态,其可能导致更新进入死循环。如果确实需要更改状态,可以尝试使用
computed或watch。 -
beforeDestroy
代码中,我通过调用
$destroy方法销毁实例,beforeDestroy先被调用。该钩子函数在实例被销毁之前被调用,此时实例仍然可用,所以相关属性仍可访问。
-
destroyed
该钩子函数在实例被销毁之后被调用。调用后,所有的事件监听器都会被移除,所有的子实例也会被销毁。此时 View 层和 Modal 层解绑,失去响应式,下面将会进一步进行验证。
-
afterDestroyed
这里所谓的
afterDestroyed并不属于 Vue 实例的生命周期,其主要是为了说明 在实例被销毁之后页面的状态。在
destroyed钩子函数中,我再次通过修改message以触发数据更新。从控制台的输出结果可以看到,实例仍然存在且可用,$data属性更新了,message被修改为The message has been changed again,但是$el并未更新,仍为<p>The message has been changed</p>,可见页面不再具有响应式。
最后,放一张官方的生命周期图示:

参考: