理解 Vue 的生命週期
前言
一直以來,其實都有多多少少使用過 Vue,這個現在很火的 Web 框架,但是不知道為甚麼,總是覺得自己都在亂用,不知道自己在幹嘛。究其原因,其實還是因為學習的不夠扎實,太多東西都不求甚解,導致對於一些基本知識的掌握不到位,當然在實際的業務場景中就肯定會處處碰壁了。本篇來學習 Vue 的生命週期,真的非常重要,希望這次務必要好好的建立起這部分的知識。
正文
Vue 生命週期概述
每個 Vue 實例在創建時都是通過 Vue()
函數的,且經過一系列的初始化過程,設置數據監聽,將實例掛載到 DOM 上並更新(如有需要)等等。同時,這個初始化的過程中會運行一些叫做 生命週期鉤子
的函數,這非常重要且強大,因為這給了用戶在不同階段添加添加代碼的機會!
Vue 的生命週期如下圖:
鉤子對於 Vue 組件就好像把人的一生從出生到死亡分成好幾個階段,好比你一定是在出生時取名字,不會到死亡時才取名字,或是如果你想在出生時就交女朋友,那也是不可能的。
Vue 組件也一樣,每個階段都有各自該做的事情,所以用特定鉤子函數做特定時想做的事,比如 Ajax 數據獲取就可以在 mounted
階段做。接下來讓我們看看到底都有哪些鉤子函數:
vue 1.0+ | vue 2.0 | Description |
---|---|---|
init | beforeCreate | 組件實例剛被創建,組件屬性計算之前,例如 data 屬性 |
created | created | 組件實例創建完成,屬性已經被綁定,但是還沒被掛到 DOM 中(尚未有 $el 屬性) |
beforeCompile | beforeMount | 模板掛載之前 |
compiled | mounted | 模板掛載之後 |
beforeUpdate | 組件更新之前 | |
updated | 組件更新之後 | |
activated | 組件被激活時調用,for keep-alive | |
deactivated | 組件被移除時調用,for keep-alive | |
beforeDestroy | beforeDestroy | 組件銷毀前調用 |
destroyed | destroyed | 組件銷毀後調用 |
整理是給已經學懂了的人看得,如果像我一樣還不是懂得很明白的人們,繼續接著下去分析每一個鉤子都做了些什麼。我們使用 Vue 框架,需要在合適的時間點做適當的事,完全了解 Vue 的生命週期,我們才能知道哪些事情該在哪個函數中做。
Vue 生命週期經歷的階段 + 鉤子函數
簡單來說,一個 Vue 實例的創建經歷很多個階段,而對於每個 Vue 組件又都根據我們的代碼而不盡相同,但是,大體來說,Vue 生命週期都會經歷以下階段:
- 開始創建
- 初始化數據
- 編譯模板
- 掛載 DOM
- 渲染 <-> 更新
- 銷毀
首先,一切的一切都是通過 new Vue()
創建一個 Vue 實例,接下來初始化事件和生命週期,這都是系統會幫我們做的,不在生命週期的範疇內,先不關心。接下來,就要進入第一個生命週期,beforeCreate
。
beforeCreate
在 Vue 實例初始化好後,這時其實所有的數據,包括 data 屬性, computed 屬性都還沒與實例真正綁定,也就是說此時根本還沒有值,也沒有真正的數據以及 DOM。通常我們可以在該鉤子添加 loading 的效果。
beforeCreate 之後,會做數據的生成,講白了也就是屬性的賦值,主要包括 data 屬性以及 computed 屬性運算。
created
這個時候,Vue 實例的屬性都有值了,但是 DOM 仍然還未生成,$el
屬性還不存在。簡單來說,就是這時候 data, computed 都做完了也都有值綁定好了,但是還沒有創建好 template 屬性對應的 html 元素,所以此時,就算更改數據,也不會觸發視圖的重新渲染。通常我們可以在這邊結束 loading 的效果,並提示用戶頁面完成之類的。
beforeMount
這個函數其實是在模板編譯(template)和數據掛載之前的鉤子。這個時候,this.$el
是有值的,但是數據還沒有掛載到頁面上,也就是說,此時頁面中的 {{...}}
還沒被替換為真實的值。
beforeMount 之後就是做模板編譯,也就是將所有數據替換模板中的內容({{...}}
)。模板編譯完後,就會進入到下一個生命週期。
mounted
這個時候,模板編譯完成,數據也都掛載完成,所以此時頁面上都可以顯示出正確的數據。一般來說,我們會在這個生命週期向後端發送異步請求(Ajax, axios 等等),獲取一些頁面渲染所需要的數據。
beforeUpdate
接下來的這個鉤子函數,是在組件更新前執行的,這也就意味著,首先要有數據更新,才會觸發該鉤子函數。而且,這邊要注意,更新的數據必須是模板上依賴的數據,否則不會,且也沒必要觸發組件更新。而所謂 beforeUpdate
則是指數據更新後,但對應的 DOM 中的 innerHTML 還未改變的時間段。
updated
該階段,組件中的數據(innerHTML)都相應的更新了,所以叫做 updated
。
activated/deactivated
這兩個鉤子看上面表格描述整理,實際上也不常用到,這邊先不做過多的探討。
beforeDestroy
顧名思義,就是在組件銷毀前觸發的鉤子函數。通常,我們可以在該鉤子函數向用戶發起 “確認刪除xxx?” 之類的提示。
destroyed
在組件銷毀後觸發的鉤子函數,通常用於回收數據,或是做一些清理工作等等。
測試 Vue 生命週期 代碼
<!DOCTYPE html>
<html>
<head>
<title>Vue 生命週期測試</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
</div>
<script type="text/javascript">
const app = new Vue({
el: '#app',
data: {
message : "Vue 生命週期"
},
beforeCreate: function() {
console.group('beforeCreate 創建前狀態 ===============》')
console.log("%c%s", "color:red" , "el : " + this.$el) //undefined
console.log("%c%s", "color:red","data : " + this.$data) //undefined
console.log("%c%s", "color:red","message: " + this.message)
},
created: function() {
console.group('created 創建完畢狀態 ===============》')
console.log("%c%s", "color:red","el : " + this.$el) //undefined
console.log("%c%s", "color:red","data : " + this.$data) //已被初始化
console.log("%c%s", "color:red","message: " + this.message) //已被初始化
},
beforeMount: function() {
console.group('beforeMount 掛載前狀態 ===============》')
console.log("%c%s", "color:red","el : " + (this.$el)) //已被初始化
console.log(this.$el)
console.log("%c%s", "color:red","data : " + this.$data) //已被初始化
console.log("%c%s", "color:red","message: " + this.message) //已被初始化
},
mounted: function() {
console.group('mounted 掛載後狀態 ===============》')
console.log("%c%s", "color:red","el : " + this.$el) //已被初始化
console.log(this.$el)
console.log("%c%s", "color:red","data : " + this.$data) //已被初始化
console.log("%c%s", "color:red","message: " + this.message) //已被初始化
},
beforeUpdate: function() {
console.group('beforeUpdate 更新前狀態 ===============》')
console.log("%c%s", "color:red","el : " + this.$el)
console.log(this.$el)
console.log("%c%s", "color:red","data : " + this.$data)
console.log("%c%s", "color:red","message: " + this.message)
},
updated: function() {
console.group('updated 更新完成狀態 ===============》')
console.log("%c%s", "color:red","el : " + this.$el)
console.log(this.$el)
console.log("%c%s", "color:red","data : " + this.$data)
console.log("%c%s", "color:red","message: " + this.message)
},
beforeDestroy: function() {
console.group('beforeDestroy 銷毀前狀態 ===============》')
console.log("%c%s", "color:red","el : " + this.$el)
console.log(this.$el)
console.log("%c%s", "color:red","data : " + this.$data)
console.log("%c%s", "color:red","message: " + this.message)
},
destroyed: function() {
console.group('destroyed 銷毀完成狀態 ===============》')
console.log("%c%s", "color:red","el : " + this.$el)
console.log(this.$el)
console.log("%c%s", "color:red","data : " + this.$data)
console.log("%c%s", "color:red","message: " + this.message)
}
})
</script>
</body>
</html>
打開控制台,我們可以看到打印結果:
注意 $el
的變化,確實在 created 階段還沒生成,但是 beforeMount 階段生成完畢。
data
的打印也表明,其實數據的創建在 created 階段
就已經完成了。
還有一個值得注意的地方,就是 {{message}}
在甚麼時候才被真正替換。在 beforeMount 階段,雖然 message 值已經生成(其實在 created 就生成好了),但是實際上還沒有替換掉 {{}}
中的值。等到 mounted 階段,才真正地將數據掛載到 DOM 上。
接下來,我們繼續測試,在控制台中輸入 app.message = '新的 Vue 生命週期'
。
可以看到,唯有模板依賴的數據發生變化,才會觸發 beforeUpdate 和 updated 鉤子函數,頁面當然也會重新渲染。
回顧 Vue 生命週期
最後在最一開始官方給的 Vue 生命週期的基礎上,添加一些總結與理解。
結語
理解 Vue 的生命週期對於使用上或是理解上都有很大的幫助,也可以更好的寫代碼以及操作頁面效果等等。都怪自己以前混水摸魚沒搞明白,現在才重學。希望看完這篇你有所收穫,若有錯誤,也歡迎前端大神們指證交流!