下面是 index.html
的模板:html
<div id="app"></div>
咱们在 new
一个 Vue 实例的过程当中,生命周期的方法就会以必定的顺序相继执行,咱们能够观察如下代码的执行结果。vue
注意:该 Vue 实例并未设置 el
属性。react
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()
两个个生命周期方法被依次执行,而其余的生命周期方法并无被触发执行。ajax
若是咱们加上 el
属性,或者调用 vm.$mount()
方法。app
new Vue({ el: '#app', // 设置 el 属性 // ... });
或者dom
var app = new Vue({ // ... }); app.$mount('#root'); // 调用 Vue 实例的 $mount() 方法
能够看到 beforeCreate()
、created()
、beforeMount()
和 mounted()
四个生命周期方法被依次执行。this
这里咱们能够知道,在 new
一个 Vue 实例的时候,若是没有设置 el
属性或者调用 Vue 实例的 $mount()
方法,实际上是只会执行 beforeCreate()
和 created()
方法。缘由在于,生命周期中的 mountd()
方法实际上是把 Vue 实例中的 template
属性里的 html
挂载到 el
属性对应的 dom
节点上,若是没有定义 el
属性或者没有调用 Vue 实例的 $mount()
方法,那么就没法执行挂载的动做,由于根本不知道要挂载到哪儿去。 spa
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()
方法中同样的。code
所以,咱们是没法在 beforeCreate()
和 created()
这两个生命周期中对 dom
作操做的,由于根本就拿不到 Vue 实例对应的那个 dom
节点,因此通常咱们是会在 mounted()
中作一些与 dom
有关的操做。
剩下的生命周期方法为何没有执行呢?接下来将介绍他们。
这两个生命周期方法只有在数据更新的时候才会触发执行。
setTimeout(() => { app.message += 1; }, 1000);
TODO...
这两个生命周期方法会在 Vue 实例被销毁的时候触发执行。
setTimeout(() => { app.$destroy(); // 该方法会销毁 Vue 实例 }, 2000);
该方法只有在开发时才会被调用,在正式打包上线时不会被调用,它的目的是帮咱们更容易地调试一些 render()
中的一些错误。
new Vue({ // ... render(h) { throw new TypeError('render error'); }, renderError(h, err) { return h('div', {}, err.stack); } });
renderError()
方法会在 render()
方法报错的时候被触发执行,页面渲染出来的内容就是错误信息。该方法只有在当前 Vue 实例的 render()
方法中出现错误的时候才会被触发,子组件的报错是不会被当前 Vue 实例捕获到的。
该方法能够用到正式环境中,帮助咱们收集一些线上的错误,而且其能够捕获到子组件的报错。
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 生命周期的一点看法,如有认为不正确或不稳当的地方,欢迎指正!