Vue 生命週期之旅
每個 Vue 實例在被創建時都要經過一系列的初始化過程——例如,需要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等。同時在這個過程中也會運行一些叫做生命週期鉤子的函數,這給了用戶在不同階段添加自己的代碼的機會
官方生命週期函數示意圖
生命週期示意圖詳解
- beforeCreate
在實例初始化之後,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用
注意是數據觀測和事件/監聽配置之前調用,也就是說在此處訪問this.$el,this.$data,實例中的屬性或方法是訪問不到的,即undefined
使用場景:可以定義一些靜態屬性或者加載動畫
- created
在實例創建完成後被立即調用。在這一步,實例已完成以下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。然而,掛載階段還沒開始,$el 屬性目前不可見
data
和methods
中的屬性和方法均可訪問,但vue
實例的$em
屬性仍然是undefined
使用場景:進行ajax請求異步數據的獲取、初始化數據,結束加載動畫
- beforeMount
在掛載開始之前被調用:相關的 render 函數首次被調用。該鉤子在服務器端渲染期間不被調用。
由示意圖可知在created和beforeMount兩個鉤子期間,首先判斷是否有el屬性,如果沒有則生命週期終止,除非手動調用vm.$mount(el)方法,
如果有el屬性,則再判斷是否存在template,如果存在則編譯template並執行render函數,如果沒有則編譯外部html作爲模板進行編譯
注意:此時的$el不再是undefined,緊緊是字符串模板,相關數據還未填充,template的編譯優先級是高於outer html的
使用場景:
- mounted
el 被新創建的 vm.$el 替換,並掛載到實例上去之後調用該鉤子。如果 root 實例掛載了一個文檔內元素,當 mounted 被調用時 vm.$el 也在文檔內。
注意 mounted 不會承諾所有的子組件也都一起被掛載。如果你希望等到整個視圖都渲染完畢,可以用 vm.$nextTick 替換掉 mounted,此時的$el爲填充後的數據
使用場景:當需要操作dom的時候執行,可以配合$.nextTick 使用進行單一事件對數據的更新後更新dom
- beforeUpdate
數據更新時調用,發生在虛擬 DOM 打補丁之前。這裏適合在更新之前訪問現有的 DOM,比如手動移除已添加的事件監聽器。
該鉤子在服務器端渲染期間不被調用,因爲只有初次渲染會在服務端進行。
使用場景:在更新之前訪問現有的 DOM,比如手動移除已添加的事件監聽器。
- updated
由於數據更改導致的虛擬 DOM 重新渲染和打補丁,在這之後會調用該鉤子。
當這個鉤子被調用時,組件 DOM 已經更新,所以你現在可以執行依賴於 DOM 的操作。然而在大多數情況下,你應該避免在此期間更改狀態。如果要相應狀態改變,通常最好使用計算屬性或 watcher 取而代之。
注意 updated 不會承諾所有的子組件也都一起被重繪。如果你希望等到整個視圖都重繪完畢,可以用 vm.$nextTick 替換掉 updated
- beforeDestroy
實例銷燬之前調用。在這一步,實例仍然完全可用。該鉤子在服務器端渲染期間不被調用。
注意:我們可以使用 vm.$destroy()進行手動銷燬
- destroyed
Vue 實例銷燬後調用。調用後,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷燬。
該鉤子在服務器端渲染期間不被調用
注意:對data的改變不會再觸發周期函數,vue實例已解除事件監聽和dom綁定,但dom結構依然存在
生命週期以及使用場景
生命週期 | 解釋 | 使用場景 |
---|---|---|
beforeCreate | 在實例初始化之後,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用 | 定義一些靜態屬性或者加載動畫,el data computed watch methods上的方法和數據均不能訪問 |
created | 在實例創建完成後被立即調用。在這一步,實例已完成以下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。然而,掛載階段還沒開始,$el 屬性目前不可見 | 初始化數據(計算或者轉換等),結束加載動畫 |
beforeMount | 在掛載開始之前被調用:相關的 render 函數首次被調用 | 此時的$el是虛擬節點 |
mounted | el 被新創建的 vm.$el 替換,並掛載到實例上去之後調用該鉤子 | 可異步請求,操作DOM |
beforeUpdate | 數據更新時調用,發生在虛擬 DOM 打補丁之前 | 這裏適合在更新之前訪問現有的DOM,比如手動移除已添加的事件監聽器 |
updated | 由於數據更改導致的虛擬 DOM 重新渲染和打補丁,在這之後會調用該鉤子 | 可執行依賴的DOM操作 |
beforeDestroy | 實例銷燬之前調用。在這一步,實例仍然完全可用 | 可用於銷燬定時器,解綁全局事件,銷燬插件對象 |
destroyed | Vue 實例銷燬後調用。調用後,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷燬 | DOM依然存在 |
測試DEMO
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<div :style="{color:color}">
<h6>{{ message + '這是在 outer html 中的' }}</h6>
</div>
</div>
</body>
<script>
const vm = new Vue({
el:'#app',
//template:"<h1>{{message +'這是在 vue 實例template中的'}}</h1>",
data:{
message:'hello world',
jumpUrl:'www.baidu.com',
color:'orange'
},
methods:{
tipTool(){
return 'hello vue lifecycle';
},
},
watch:{
color:(new_value , old_value) => {
alert('字體顏色監聽事件');
}
},
beforeCreate:function(){
//在實例初始化之後,數據觀測 (data observer) 和 event/watcher 事件配置之前被調用。
this.refererUrl = 'www.sina.com';
console.group('vue 生命週期 beforeCreate');
console.log('vue實例的el',this.$el);
console.log('vue實例的data',this.$data);
console.log('vue實例的data中的數據 message ',this.message);
console.log('vue實例的data中的數據 refererUrl ',this.refererUrl);
const res = this.tipTool();
console.log('vue實例的方法 ',res);
},
created:function(){
// 在實例創建完成後被立即調用。在這一步,
// 實例已完成以下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。
// 然而,掛載階段還沒開始,$el 屬性目前不可見。
console.group('vue 生命週期 created');
console.log('vue實例的el',this.$el);
console.log('vue實例的data',this.$data);
console.log('vue實例的data中的數據 message ',this.message);
console.log('vue實例的data中的數據 refererUrl ',this.refererUrl);
const res = this.tipTool();
console.log('vue實例的方法 ',res);
},
beforeMount(){
// 在掛載開始之前被調用:相關的 render 函數首次被調用。
console.group('vue 生命週期 beforeMount');
console.log('vue實例的el',this.$el);
console.log('vue實例的data',this.$data);
console.log('vue實例的data中的數據 message ',this.message);
console.log('vue實例的data中的數據 refererUrl ',this.refererUrl);
const res = this.tipTool();
console.log('vue實例的方法 ',res);
},
mounted(){
// el 被新創建的 vm.$el 替換,並掛載到實例上去之後調用該鉤子。
// 如果 root 實例掛載了一個文檔內元素,當 mounted 被調用時 vm.$el 也在文檔內。
// 注意 mounted 不會承諾所有的子組件也都一起被掛載。如果你希望等到整個視圖都渲染完畢,可以用 vm.$nextTick 替換掉 mounted
console.group('vue 生命週期 mounted');
console.log('vue實例的el',this.$el);
console.log('vue實例的data',this.$data);
console.log('vue實例的data中的數據 message ',this.message);
console.log('vue實例的data中的數據 refererUrl ',this.refererUrl);
const res = this.tipTool();
console.log('vue實例的方法 ',res);
},
beforeUpdate(){
// 數據更新時調用,發生在虛擬 DOM 打補丁之前。這裏適合在更新之前訪問現有的 DOM,比如手動移除已添加的事件監聽器。
// 該鉤子在服務器端渲染期間不被調用,因爲只有初次渲染會在服務端進行
console.group('vue 生命週期 beforeUpdate');
console.log('vue實例的el',this.$el);
console.log('vue實例的data',this.$data);
console.log('vue實例的data中的數據 message ',this.message);
console.log('vue實例的data中的數據 refererUrl ',this.refererUrl);
const res = this.tipTool();
console.log('vue實例的方法 ',res);
},
updated(){
// 由於數據更改導致的虛擬 DOM 重新渲染和打補丁,在這之後會調用該鉤子。
// 當這個鉤子被調用時,組件 DOM 已經更新,所以你現在可以執行依賴於 DOM 的操作。然而在大多數情況下,你應該避免在此期間更改狀態。如果要相應狀態改變,通常最好使用計算屬性或 watcher 取而代之。
// 注意 updated 不會承諾所有的子組件也都一起被重繪。如果你希望等到整個視圖都重繪完畢,可以用 vm.$nextTick 替換掉 updated
console.group('vue 生命週期 updated');
console.log('vue實例的el',this.$el);
console.log('vue實例的data',this.$data);
console.log('vue實例的data中的數據 message ',this.message);
console.log('vue實例的data中的數據 refererUrl ',this.refererUrl);
const res = this.tipTool();
console.log('vue實例的方法 ',res);
},
beforeDestroy(){
//實例銷燬之前調用。在這一步,實例仍然完全可用
console.group('vue 生命週期 beforeDestroy');
console.log('vue實例的el',this.$el);
console.log('vue實例的data',this.$data);
console.log('vue實例的data中的數據 message ',this.message);
console.log('vue實例的data中的數據 refererUrl ',this.refererUrl);
const res = this.tipTool();
console.log('vue實例的方法 ',res);
},
destroyed(){
//Vue 實例銷燬後調用。調用後,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷燬
console.group('vue 生命週期 destroyed');
console.log('vue實例的el',this.$el);
console.log('vue實例的data',this.$data);
console.log('vue實例的data中的數據 message ',this.message);
console.log('vue實例的data中的數據 refererUrl ',this.refererUrl);
const res = this.tipTool();
console.log('vue實例的方法 ',res);
}
});
</script>
</html>