在 Vue 中,當一個組件創建時,它會經歷一系列的流程,這些流程決定了組件從初始化到渲染,再到最終銷毀的過程。
我們稱這個過程為組件的「生命週期」。生命週期包括了 Vue Template 的編譯、渲染、更新等階段,最終將結果顯示在 HTML 上。
大致可以分成四個階段:
methods
中使用 $nextTick
確保 DOM 完成更新後執行後續操作)請勿在此階段更改 Vue 響應式數據的狀態,將會造成無窮迴圈。
官方生命週期流程圖:
主要的核心功能如下:
這邊透過v-if
切換組件來讓大家更理解生命週期
的變化(請搭配以下範例繼續閱讀):
透過v-if
指令觀察child-component
組件創建及銷毀。child-component
組件內容則是包含一個計數器,用來觀察updated
的生命週期變化。
根組件 Vue Template:
<child-component v-if="isShowComponent"></child-component>
子組件(child-component) Vue Template:
<div ref="ComponentTitle">{{ title }}</div>
<div>當前 Count:{{ count }}</div>
<button @click="add" type="button">+1</button>
⭐ ref
的部分可以視作模板的標記,為了讀取其實際DOM
元素使用。
當前案例開始執行的流程總共觸發以下四個生命週期:
1. (沒辦法讀取實體數據)beforeCreate lifecircle this.title undefined
2. (有辦法讀取實體數據)created lifecircle this.title 我是 childComponent
3. (沒辦法讀取模板標記的 DOM 元素)beforeMount lifecircle this.$refs.ComponentTitle undefined
4. (有辦法讀取模板標記的 DOM 元素)mounted lifecircle this.$refs.ComponentTitle div
當點擊 component 中的按鈕【+1】時,觸發beforeupdate
跟updated
兩個生命週期:
1. (畫面數據尚未更新)beforeUpdate lifecircle DOM content: 當前 Count:0
2. (畫面數據已更新)updated lifecircle DOM content: 當前 Count:1
當點擊按鈕【不顯示 Component】調整v-if
綁定的變數isShowComponent
為 false 時,會觸發beforeUnmount
及unmounted
兩個生命週期:
1. (有辦法讀取模板標記的 DOM 元素)beforeUnmount lifecircle this.$refs.ComponentTitle <div>我是 childComponent</div>
2. (沒辦法讀取模板標記的 DOM 元素)unmounted lifecircle this.$refs.ComponentTitle null
在Vue
的生命週期中,若要捕捉 DOM 元素的更新,應使用updated
生命週期。響應式變數在 Vue 中並非即時更新,當你修改響應式狀態時,變更不會立刻反映到 DOM 上。Vue 會將這些變更暫時緩存,並在下一個更新周期(即 "next tick")中統一批量更新 DOM,從而保證性能和渲染效率。
以下案例說明了修改響應式變數時因延遲更新所產生的問題:
👉 Vue3 響應式變數-非同步更新使用實作連結
流程說明:
$nextTick
的情況下,會發現元素還未新增完成,卷軸就已到底部this.scrollToBottom()
函數執行的過程改動在$nextTick
內執行,查看變化javascript:
const rootComponent = {
// 數據(text:輸入內容、productList:顯示清單)
data() {
return {
text: "",
productList: ["apple", "banan", "orange"]
};
},
methods: {
// 將輸入框內容新增至 productList 清單中
insertItem() {
this.productList.push(this.text);
this.text = "";
// 不加 $nextTick 寫法
this.scrollToBottom();
// ---
// 加$nextTick(確保DOM畫面已更新)
// this.$nextTick(() => {
// this.scrollToBottom();
// });
},
scrollToBottom() {
const dom = this.$refs.content;
dom.scrollTop = dom.scrollHeight;
}
}
};
不加 $nextTick 寫法呈現方式:
發現問題了嗎?雖然捲軸已經出現,但它並沒有如預期那樣捲動到底部。實際上,確實執行了捲動到底部的操作,但由於這個操作在新增項目之前就發生了,導致最終的效果沒有達到預期。
加 $nextTick 寫法呈現方式:
在這種情況下,你可以使用 Vue 提供的$nextTick API
,來等待 DOM 更新完成後再執行捲軸操作。這樣可以確保捲軸操作發生在 DOM 更新完畢之後,從而達到預期的效果。
errorCaptured
是 Vue 組件生命週期中的一個鉤子函數,用來捕捉子組件中發生的錯誤。當某個子組件內部發生錯誤時,父組件的 errorCaptured 鉤子會被觸發,從而可以攔截並處理該錯誤。
這個錯誤捕捉的函式主要有兩個功能:
這邊透過子組件發送一個錯誤,讓根組件接收這個錯誤:
👉 Vue3 Options API errorCaptured hook 實作連結
全局錯誤處理器設置:當 Vue 應用中發生錯誤且未被捕捉或處理時,這些錯誤最終會被全局錯誤處理器捕捉和處理。
app.config.errorHandler = (err, vm, info) =>{
console.log('全域設定', err)
}
根組件 Vue Template:
<child-component></child-component>
<p>根組件錯誤訊息:{{ errorMsg }}</p>
根組件 javascript:
errorCaptured(err, vm, info) {
console.log('')
this.errorMsg = `錯誤訊息:${err},錯誤資訊:${info}`;
// 如果返回 false,錯誤不會繼續冒泡
// return false
}
子組件 Vue Template:
<button @click="sendError" type="button">發送錯誤</button>
子組件 javascript:
methods: {
sendError() {
throw new Error("這是 childComponent 的錯誤");
}
}
已完成數據初始化
。可以操作對應的 DOM 元素
。讀取到更新後的數據
以及重新渲染的 DOM 元素。無法再讀取對應的 DOM 元素
。響應式變數
的更新
並不是同步
的,Vue 會在下一個 DOM 更新週期批量更新變更。因此,當需要在 DOM 更新完成後執行操作時,應使用$nextTick
來確保 DOM 已經更新,從而避免操作失敗或不達預期。false
阻止錯誤繼續向上冒泡傳遞。