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 生命週期的一點看法,如有認爲不正確或不穩當的地方,歡迎指正!

相關文章
相關標籤/搜索