Vue 生命周期
下面是 index.html
的模板:
<div id="app"></div>
Vue 生命周期方法的调用顺序 / 生命周期方法在什么情况下被触发
我们在 new
一个 Vue 实例的过程中,生命周期的方法就会以一定的顺序相继执行,我们可以观察以下代码的执行结果。
注意:该 Vue 实例并未设置 el
属性。
new Vue({
data: {
message: 0
},
template: '<div>{{ message }}</div>',
beforeCreate() {
console.log(this.$el, 'beforeCreate');
},
created() {
console.log(this.$el, 'created');
},
beforeMount() {
console.log(this.$el, 'beforeMount');
},
mounted() {
console.log(this.$el, 'mounted');
},
beforeUpdate() {
console.log(this.$el, 'beforeUpdate');
},
updated() {
console.log(this.$el, 'updated');
},
activated() {
console.log(this.$el, 'activated');
},
deactivated() {
console.log(this.$el, 'deactivated');
},
beforeDestroy() {
console.log(this.$el, 'beforeDestroy');
},
destroyed() {
console.log(this.$el, 'destroyed');
},
errorCaptured() {
console.log(this.$el, 'errorCaptured');
}
});
我们可以看到 beforeCreate()
、created()
两个个生命周期方法被依次执行,而其他的生命周期方法并没有被触发执行。
如果我们加上 el
属性,或者调用 vm.$mount()
方法。
new Vue({
el: '#app', // 设置 el 属性
// ...
});
或者
var app = new Vue({
// ...
});
app.$mount('#root'); // 调用 Vue 实例的 $mount() 方法
可以看到 beforeCreate()
、created()
、beforeMount()
和 mounted()
四个生命周期方法被依次执行。
这里我们可以知道,在 new
一个 Vue 实例的时候,如果没有设置 el
属性或者调用 Vue 实例的 $mount()
方法,其实是只会执行 beforeCreate()
和 created()
方法。原因在于,生命周期中的 mountd()
方法其实是把 Vue 实例中的 template
属性里的 html
挂载到 el
属性对应的 dom
节点上,如果没有定义 el
属性或者没有调用 Vue 实例的 $mount()
方法,那么就无法执行挂载的动作,因为根本不知道要挂载到哪儿去。
beforeCreate()
、created()
、beforeMount()
和 mounted()
四个方法都是一次性地,在 Vue 实例的生命周期中它们只会被触发调用一次。并且,beforeMount()
和 mounted()
两个方法在服务端渲染的时候是不会被调用的,因为这两个方法是与 dom
的操作有一定关系,而服务端中没有 dom
执行的环境,所以也就不会有beforeMount()
和 mounted()
了。
同时,可以看到 vm.$el
在各个生命周期中的值是不同的,在 beforeCreate()
和 created()
中是 undefined
,在 beforeMount()
中变成了 index.html
里的 <div id="app"></div>
,等到执行 mounted()
方法时则又变成了渲染之后 <div>0</div>
。在 mounted()
方法之后,我们再调用生命周期的方法拿到的 vm.$el
都是跟 mounted()
方法中一样的。
因此,我们是无法在 beforeCreate()
和 created()
这两个生命周期中对 dom
做操作的,因为根本就拿不到 Vue 实例对应的那个 dom
节点,所以一般我们是会在 mounted()
中做一些与 dom
有关的操作。
剩下的生命周期方法为什么没有执行呢?接下来将介绍他们。
beforeUpdate() 和 updated()
这两个生命周期方法只有在数据更新的时候才会触发执行。
setTimeout(() => {
app.message += 1;
}, 1000);
activated() 和 deactivated()
TODO...
beforeDestroy() 和 destroyed()
这两个生命周期方法会在 Vue 实例被销毁的时候触发执行。
setTimeout(() => {
app.$destroy(); // 该方法会销毁 Vue 实例
}, 2000);
renderError()
该方法只有在开发时才会被调用,在正式打包上线时不会被调用,它的目的是帮我们更容易地调试一些 render()
中的一些错误。
new Vue({
// ...
render(h) {
throw new TypeError('render error');
},
renderError(h, err) {
return h('div', {}, err.stack);
}
});
renderError()
方法会在 render()
方法报错的时候被触发执行,页面渲染出来的内容就是错误信息。该方法只有在当前 Vue 实例的 render()
方法中出现错误的时候才会被触发,子组件的报错是不会被当前 Vue 实例捕获到的。
errorCaptured()
该方法可以用到正式环境中,帮助我们收集一些线上的错误,并且其可以捕获到子组件的报错。
new Vue({
// ...
errorCaptured(h, err) {
// ...
}
});
生命周期图示解析
下面是 Vue 的生命周期的图示,接下来将对其进行简单的解析。
在 new
一个 Vue 实例的过程中,首先执行了 init()
方法,在 init()
方法的 Events & LifeCycle
之后 执行 beforeCreate()
方法,在 init()
方法的 injections & reactivity
之后执行 created
方法。可以看到,在执行 beforeCreate()
的时候,Vue 实例的事件绑定是已经完成的了,但是它的响应式还没有,所以我们不在 beforeCreate()
中去修改 data
里面的数据,如果你要做一些 ajax
请求,然后给 Vue 实例的 data
赋新值,最早也要放在 created()
中来做。
接下来是会判断是否设置了 el
属性,如果有,则会去判断是否有 template
属性,如果没有,则会等到调用了 vm.$mount()
方法之后再去判断是否有 template
属性。
如果设置了 template
属性,会将 template
解析为一个 render()
方法,如果没有设置,会将 el
属性对应的 html
解析为 template
。这一步可以不设置 template
属性,通过手动调用 render()
方法来实现。
new Vue({
// ...
render(h) {
return h('div', {}, this.message);
}
});
render()
方法接收一个入参,该入参是一个创建虚拟节点的方法,也就是说,render()
方法会返回一个虚拟节点。render()
方法会在 beforeMount()
与 mounted()
之间执行,创建 vm.$el
属性并且替换掉 el
属性对应的 html
节点,这就解释了前面的内容,在 beforeMount()
中 vm.$el
为 index.html
中的 <div id="app"></div>
,在 mounted()
中 vm.$el
变成了渲染 template
后的 <div>0</div>
,这中间就是调用了 render()
方法。用这种方法来做,与设置 template
属性的结果是一样的。
在用 .vue
文件开发的过程中,是没有在 Vue 实例中设置 template
属性的,而是在 .vue
文件中编写了 <template></template>
标签,再经过 vue-loader 处理,直接解析为 render()
方法,这个方式有一个好处。因为解析 Vue 实例中的 template
属性为 render()
方法是一个比较耗时的过程,使用 vue-loader 来帮我们处理,会使得在页面上执行 vue 代码的效率更高。
在 mounted()
执行之后,这个 Vue 实例其实就已经初始化完成了。后续的生命周期都是需要一些外部的触发才会执行的,比如,有数据更新时会调用 beforeUpdate()
和 updated()
,Vue 实例被销毁时会调用 beforeDestroy()
和 destroyed()
。
这就是一个 Vue 实例从新建到销毁的整个流程。
以上是我对 Vue 生命周期的一点见解,若有认为不正确或不妥当的地方,欢迎指正!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。