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