Vue 生命周期浅析

Vue 生命周期

下面是 index.html 的模板:html

<div id="app"></div>

Vue 生命周期方法的调用顺序 / 生命周期方法在什么状况下被触发

咱们在 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');
    }
});

实例化 Vue 时的生命周期执行结果(未设置 el 属性)

咱们能够看到 beforeCreate()created() 两个个生命周期方法被依次执行,而其余的生命周期方法并无被触发执行。ajax

若是咱们加上 el 属性,或者调用 vm.$mount()方法。app

new Vue({
    el: '#app', // 设置 el 属性
    // ...
});

或者dom

var app = new Vue({
    // ...
});

app.$mount('#root'); // 调用 Vue 实例的 $mount() 方法

实例化 Vue 时的生命周期执行结果

能够看到 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 有关的操做。

剩下的生命周期方法为何没有执行呢?接下来将介绍他们。

beforeUpdate() 和 updated()

这两个生命周期方法只有在数据更新的时候才会触发执行。

setTimeout(() => {
    app.message += 1;
}, 1000);

数据更新时生命周期执行结果

activated() 和 deactivated()

TODO...

beforeDestroy() 和 destroyed()

这两个生命周期方法会在 Vue 实例被销毁的时候触发执行。

setTimeout(() => {
    app.$destroy(); // 该方法会销毁 Vue 实例
}, 2000);

销毁 Vue 实例时生命周期执行结果

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.$elindex.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 生命周期的一点看法,如有认为不正确或不稳当的地方,欢迎指正!

相关文章
相关标签/搜索