iT邦幫忙

2024 iThome 鐵人賽

DAY 14
1
Modern Web

Vue 3 初學者:用實作帶你看過核心概念系列 第 14

Vue 3 用實作帶你看過核心概念 - Day 14:生命週期鉤子(Lifecycle Hooks)

  • 分享至 

  • xImage
  •  

文章背景圖

目錄

  • 核心生命週期鉤子
  • Options API 中的響應式狀態延遲更新 - 使用 $nextTick 確保 DOM 同步
  • errorCaptured - 錯誤捕捉鉤子
  • 總結

核心生命週期鉤子

在 Vue 中,當一個組件創建時,它會經歷一系列的流程,這些流程決定了組件從初始化到渲染,再到最終銷毀的過程。

我們稱這個過程為組件的「生命週期」。生命週期包括了 Vue Template 的編譯、渲染、更新等階段,最終將結果顯示在 HTML 上。

大致可以分成四個階段:

  1. 建立 Vue 實體與綁定關係:
    • beforeCreate:Vue 實體已經被創建,但數據(data)和事件(methods)尚未初始化。
    • created:Vue 實體的數據和事件初始化已完成。
  2. 模板編譯與掛載:
    • beforeMount:Vue 實體尚未與 DOM 節點綁定,模板也還未被渲染。
    • mounted:Vue 實體與 DOM 節點完成綁定,模板已渲染並掛載到頁面上。
  3. 狀態變更與更新:
    • beforeUpdate:Vue 實體的狀態發生改變,畫面尚未更新。
    • updated:Vue 實體的狀態已改變,畫面同步完成更新。(可在methods中使用 $nextTick確保 DOM 完成更新後執行後續操作)請勿在此階段更改 Vue 響應式數據的狀態,將會造成無窮迴圈。
  4. Vue 實體銷毀:
    • beforeUnmount:當 Vue 實體即將從 DOM 中移除時觸發。
    • unmounted:Vue 實體已經從 DOM 中完全移除並解除綁定。此時可以在此階段進行清理工作,如解除事件監聽或銷毀定時器等。

官方生命週期流程圖:
官方生命週期流程圖

主要的核心功能如下:
核心生命週期勾子功能

這邊透過v-if切換組件來讓大家更理解生命週期的變化(請搭配以下範例繼續閱讀):

👉 Vue3 Options API 生命週期實作連結

透過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】時,觸發beforeupdateupdated兩個生命週期:

  • updated 階段:能夠讀取到模板變數更新且渲染成頁面的 DOM 元素。
1. (畫面數據尚未更新)beforeUpdate lifecircle DOM content: 當前 Count:0

2. (畫面數據已更新)updated lifecircle DOM content: 當前 Count:1

當點擊按鈕【不顯示 Component】調整v-if綁定的變數isShowComponent為 false 時,會觸發beforeUnmountunmounted兩個生命週期:

  • 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 範例輸入框輸入內容

流程說明:

  1. 輸入框輸入文字後點擊【新增項目】送出後讓顯示文字區域出現捲軸並且將捲軸捲到底部
  2. 不使用$nextTick的情況下,會發現元素還未新增完成,卷軸就已到底部
  3. 將原本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 響應式變數改動與畫面渲染不一致

發現問題了嗎?雖然捲軸已經出現,但它並沒有如預期那樣捲動到底部。實際上,確實執行了捲動到底部的操作,但由於這個操作在新增項目之前就發生了,導致最終的效果沒有達到預期。

加 $nextTick 寫法呈現方式

加$nextTick 響應式變數改動與畫面渲染不一致

在這種情況下,你可以使用 Vue 提供的$nextTick API,來等待 DOM 更新完成後再執行捲軸操作。這樣可以確保捲軸操作發生在 DOM 更新完畢之後,從而達到預期的效果。

errorCaptured - 錯誤捕捉鉤子

errorCaptured是 Vue 組件生命週期中的一個鉤子函數,用來捕捉子組件中發生的錯誤。當某個子組件內部發生錯誤時,父組件的 errorCaptured 鉤子會被觸發,從而可以攔截並處理該錯誤。

這個錯誤捕捉的函式主要有兩個功能:

  • 對子組件的錯誤進行處理。
  • 選擇是否繼續向上冒泡該錯誤。若不希望錯誤冒泡,返回 false 即可阻止錯誤繼續向上傳遞。

這邊透過子組件發送一個錯誤,讓根組件接收這個錯誤:

👉 Vue3 Options API errorCaptured hook 實作連結

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 元素
  • $nextTick 的應用與 DOM 更新機制響應式變數更新不是同步的,Vue 會在下一個 DOM 更新週期批量更新變更。因此,當需要在 DOM 更新完成後執行操作時,應使用$nextTick來確保 DOM 已經更新,從而避免操作失敗或不達預期。
  • errorCaptured 鉤子:錯誤捕捉與處理機制:只能捕捉子組件內部所發生的錯誤,可以透過是否回傳false 阻止錯誤繼續向上冒泡傳遞。

上一篇
Vue 3 用實作帶你看過核心概念 - Day 13:v-model 雙向綁定控制 - 常用於表單輸入
下一篇
Vue 3 用實作帶你看過核心概念 - Day 15:監聽器(Watchers)
系列文
Vue 3 初學者:用實作帶你看過核心概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言