說到生命週期函數,就要來先談談生命週期,一個Vue實例從開始創建、初始化數據、編譯模板、掛載Dom、渲染→更新→渲染、銷燬等一系列過程,我們稱之爲Vue的生命週期
;
更通俗的說實例從被創建出來、運行、最後被銷燬所要經歷的一系列過程,就是生命週期。
在Vue生命週期的不同階段,內部會自動執行一些稱爲生命週期鉤子的函數,也稱之爲生命週期函數,也叫生命週期鉤子,生命週期事件。
這些生命週期鉤子可以在Vue的選項中配置,如下:
<div id="app">
<input type="text" v-model="message">
<button v-on:click="handleDestroy">銷燬</button>
<p>{{message}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: 'Hello world',
},
methods: {
handleDestroy() {
this.$destroy();
}
},
beforeCreate() {
console.groupCollapsed("%c%s", "color:red", '1、實例創建之前: beforeCreate');
console.log("$el:", this.$el,);
console.log("$data:", this.$data);
console.groupEnd();
},
created() {
console.groupCollapsed("%c%s", "color:red", '2、實例創建完畢: created');
console.log("$el:", this.$el);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
beforeMount() {
console.groupCollapsed("%c%s", "color:red", '3、實例掛載之前: beforeMount');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
mounted() {
console.groupCollapsed("%c%s", "color:red", '4、實例掛載完畢: mounted');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
beforeUpdate() {
console.groupCollapsed("%c%s", "color:red", '5、實例更新之前: beforeUpdate');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
updated() {
console.groupCollapsed("%c%s", "color:red", '6、實例更新完畢:updated');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
beforeDestroy() {
console.groupCollapsed("%c%s", "color:red", '7、實例銷燬之前:beforeDestroy');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
},
destroyed() {
console.groupCollapsed("%c%s", "color:red", '8、實例銷燬完畢:destroyed');
console.log("$el:", this.$el.innerHTML);
console.log("$data:", JSON.stringify(this.$data));
console.groupEnd();
}
});
</script>
上面的例子中,如果在生命週期的鉤子函數中打斷點觀察調用棧和作用域,更容易理解Vue的生命週期,在生命週期鉤子中我們看到Vue內部執行了很多方法,其結果是實例的this在不斷的變化。
生命週期的三個階段
一、創建階段
1.初始化事件和生命週期
實例、組件通過new Vue() 創建出來之後會初始化事件和生命週期
完成後,就會執行beforeCreate
鉤子函數,這個時候,數據還沒有掛載,無法訪問到數據和真實的dom。
2. 初始化注入和校驗
實例完成數據觀測 (data observer), 屬性和方法的運算,watch/event 事件回調。但掛載階段還沒開始,$el 屬性目前不可見。
完成後,就會執行created
鉤子函數,created函數是最早能夠調用data和methods數據的地方,更改數據不會觸發其他的鉤子函數,一般可以在這裏獲取初始數據。
3. 找實例或者組件對應的模板,編譯模板爲虛擬Dom放入到render函數中準備渲染
① 判斷實例裏是否有el選項
- 存在el,實例將立即進入編譯過程;
- 不存在el,需要顯式調用 vm.$mount() 手動開啓編譯。
② 判斷實例裏是否有template選項
- 沒有,將el掛載點的HTML作爲template進行頁面渲染;
- 有,使用實例裏面的template進行頁面渲染
完成後,就會執行beforeMount
鉤子函數,這個函數中虛擬Dom已經創建完成,準備渲染,在這裏也可以更改數據,不會觸發數據更新鉤子。
5. 開始render,渲染出真實dom
完成後,就會執行mounted
鉤子函數,這個時候,組件已經出現在頁面中,數據、真實dom都已經處理好了,事件都已經掛載好了。
二、運行階段
-
如果組件或實例的數據被更改,會立即執行
beforeUpdate
鉤子函數,鉤子函數執行後後Vue的虛擬Dom機制會重新構建虛擬Dom與上一次的虛擬Dom樹利用diff算法進行對比之後重新渲染。
-
當更新完成後,會執行
updated
鉤子函數執行,此時數據已經更改完成,Dom也重新render完成。
三、銷燬階段
-
當經調用$destroy方法後,立即執行
beforeDestroy
鉤子。 -
銷燬實例與DOM之間的關聯,解綁它的全部指令及事件監聽器(並不能清除已有的頁面上的DOM),之後會執行
destroyed
鉤子。
beforeDestroy和destroyed用法相似,一般在這裏做善後工作,如清除計時器、清除非指令綁定的事件等等
PS:銷燬實例與DOM之間的關聯,解綁它的全部指令及事件監聽器,並不能清除已有的頁面上的DOM。
vue中父級與子組件生命週期的先後順序
修改一下上面的例子,在實例中掛載一個子組件,通過這段代碼來看一下父級與子組件生命週期的先後順序
<div id="app">
<input type="text" v-model="message">
<button v-on:click="handleDestroy">銷燬父組件</button>
<button v-on:click="flag=!flag">銷燬/重建子組件</button>
<child-a :msg="message" inline-template v-if="flag">
<div class="child-a">
<label>{{msg}}</label><input type="text" v-model="title">
</div>
</child-a>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
message: '父組件信息',
flag: true
},
methods: {
handleDestroy() {
this.$destroy();
}
},
components: {
'childA': {
data() {
return {
title: '子組件',
}
},
props: ['msg'],
beforeCreate() {
console.log("%c%s", "color:green", '(1)、子組件 beforeCreate');
},
created() {
console.log("%c%s", "color:green", '(2)、子組件 created');
},
beforeMount() {
console.log("%c%s", "color:green", '(3)、子組件 beforeMount');
},
mounted() {
console.log("%c%s", "color:green", '(4)、子組件 mounted');
},
beforeUpdate() {
console.log("%c%s", "color:green", '(5)、子組件 beforeUpdate');
},
updated() {
console.log("%c%s", "color:green", '(6)、子組件 updated ');
},
beforeDestroy() {
console.log("%c%s", "color:green", '(7)、子組件 beforeDestroy ');
},
destroyed() {
console.log("%c%s", "color:green", '(8)、子組件 destroyed ');
}
}
},
beforeCreate() {
console.log("%c%s", "color:red", '1、實例創建之前: beforeCreate');
},
created() {
console.log("%c%s", "color:red", '2、實例創建完畢: created');
},
beforeMount() {
console.log("%c%s", "color:red", '3、實例掛載之前: beforeMount');
},
mounted() {
console.log("%c%s", "color:red", '4、實例掛載完畢: mounted');
},
beforeUpdate() {
console.log("%c%s", "color:red", '5、實例更新之前: beforeUpdate');
},
updated() {
console.log("%c%s", "color:red", '6、實例更新完畢:updated');
},
beforeDestroy() {
console.log("%c%s", "color:red", '7、實例銷燬之前:beforeDestroy');
},
destroyed() {
console.log("%c%s", "color:red", '8、實例銷燬完畢:destroyed');
}
});
</script>
1、當父組件執行完beforeMount
,會依次執行子組件的生命週期鉤子,直到全部子組件掛載到實例上(執行完子組件的mounted鉤子),父組件纔會進入mounted鉤子。
2、更新子組件的數據,不會觸發父組件的生命週期鉤子
3、如果子組件引用了父組件的數據,父組件數據更新時,執行順序是:
4、銷燬父組件時,同時銷燬其包含的子組件,同前面說的一樣,子組件的視圖也不會消失
5、本例中,通過v-if的方法銷燬子組件時,同時會觸發父組件的更新鉤子