Vue 3 用實作帶你看過核心概念 - Day 14:生命週期鉤子(Lifecycle Hooks)
3.8k 字,約 4 分鐘
2024 iThome 鐵人賽:Vue 3 初學者:用實作帶你看過核心概念。 原文發表於 iThome,也可以在下方留言。
目錄
- 核心生命週期鉤子
- Options API 中的響應式狀態延遲更新 - 使用 $nextTick 確保 DOM 同步
- errorCaptured - 錯誤捕捉鉤子
- 總結
核心生命週期鉤子
在 Vue 中,當一個組件創建時,它會經歷一系列的流程,這些流程決定了組件從初始化到渲染,再到最終銷毀的過程。
我們稱這個過程為組件的「生命週期」。生命週期包括了 Vue Template 的編譯、渲染、更新等階段,最終將結果顯示在 HTML 上。
大致可以分成四個階段:
- 建立 Vue 實體與綁定關係:
- beforeCreate:Vue 實體已經被創建,但數據(data)和事件(methods)尚未初始化。
- created:Vue 實體的數據和事件初始化已完成。
- 模板編譯與掛載:
- beforeMount:Vue 實體尚未與 DOM 節點綁定,模板也還未被渲染。
- mounted:Vue 實體與 DOM 節點完成綁定,模板已渲染並掛載到頁面上。
- 狀態變更與更新:
- beforeUpdate:Vue 實體的狀態發生改變,畫面尚未更新。
- updated:Vue 實體的狀態已改變,畫面同步完成更新。(可在
methods中使用$nextTick確保 DOM 完成更新後執行後續操作)請勿在此階段更改 Vue 響應式數據的狀態,將會造成無窮迴圈。
- Vue 實體銷毀:
- beforeUnmount:當 Vue 實體即將從 DOM 中移除時觸發。
- unmounted:Vue 實體已經從 DOM 中完全移除並解除綁定。此時可以在此階段進行清理工作,如解除事件監聽或銷毀定時器等。
官方生命週期流程圖:
主要的核心功能如下:
這邊透過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元素使用。
當前案例開始執行的流程總共觸發以下四個生命週期:
- created 階段:能夠讀取到 Vue 實例中定義的數據及方法。
- mouted 階段:能夠讀取到模板渲染成網頁的 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兩個生命週期:
- updated 階段:能夠讀取到模板變數更新且渲染成頁面的 DOM 元素。
1. (畫面數據尚未更新)beforeUpdate lifecircle DOM content: 當前 Count:0
2. (畫面數據已更新)updated lifecircle DOM content: 當前 Count:1
當點擊按鈕【不顯示 Component】調整v-if綁定的變數isShowComponent為 false 時,會觸發beforeUnmount及unmounted兩個生命週期:
- unmounted 階段:Vue 實體與 DOM 元素的綁定關係被解除,$refs 也會變為 null,無法再讀取對應的 DOM 元素。
1. (有辦法讀取模板標記的 DOM 元素)beforeUnmount lifecircle this.$refs.ComponentTitle <div>我是 childComponent</div>
2. (沒辦法讀取模板標記的 DOM 元素)unmounted lifecircle this.$refs.ComponentTitle null
Options API 中的響應式狀態延遲更新 - 使用 $nextTick 確保 DOM 同步
在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 - 錯誤捕捉鉤子
errorCaptured是 Vue 組件生命週期中的一個鉤子函數,用來捕捉子組件中發生的錯誤。當某個子組件內部發生錯誤時,父組件的 errorCaptured 鉤子會被觸發,從而可以攔截並處理該錯誤。
這個錯誤捕捉的函式主要有兩個功能:
- 對子組件的錯誤進行處理。
- 選擇是否繼續向上冒泡該錯誤。若不希望錯誤冒泡,返回 false 即可阻止錯誤繼續向上傳遞。
這邊透過子組件發送一個錯誤,讓根組件接收這個錯誤:
👉 Vue3 Options API errorCaptured hook 實作連結
全局錯誤處理器設置:當 Vue 應用中發生錯誤且未被捕捉或處理時,這些錯誤最終會被全局錯誤處理器捕捉和處理。
- err:錯誤對象
- vm:發生錯誤 Vue 實例
- info:錯誤訊息(生產環境是代碼方式呈現)
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 的錯誤");
}
}
總結
- 核心生命週期:
- created:可以讀取到 Vue 實例中的數據、方法、計算屬性等,此階段
已完成數據初始化。 - mouted:模板已經完成渲染並掛載到頁面,
可以操作對應的 DOM 元素。 - updated:當響應式數據改變時觸發,這時可以
讀取到更新後的數據以及重新渲染的 DOM 元素。 - unmounted:Vue 實例及其綁定的響應式狀態已經解除,
無法再讀取對應的 DOM 元素。
- created:可以讀取到 Vue 實例中的數據、方法、計算屬性等,此階段
- $nextTick 的應用與 DOM 更新機制:
響應式變數的更新並不是同步的,Vue 會在下一個 DOM 更新週期批量更新變更。因此,當需要在 DOM 更新完成後執行操作時,應使用$nextTick來確保 DOM 已經更新,從而避免操作失敗或不達預期。 - errorCaptured 鉤子:錯誤捕捉與處理機制:只能捕捉子組件內部所發生的錯誤,可以透過是否回傳
false阻止錯誤繼續向上冒泡傳遞。
留言
留言使用 GitHub 帳號登入(Giscus),留言會存在 GitHub Discussions。