本文 AI 產出,尚未審核

Vue3 Options API(傳統寫法)── 生命周期(beforeCreate、mounted、updated、destroyed)

簡介

在 Vue3 中,即使已經推出了 Composition API,Options API 仍是許多既有專案與新手入門的首選寫法。
Vue 元件的生命周期 (Lifecycle),是指元件從建立、掛載、更新到銷毀的完整過程。掌握每個階段的 Hook,能讓你在正確的時機執行資料初始化、DOM 操作、資源釋放等工作,提升程式的可維護性與效能。

本篇文章將深入探討 beforeCreate、mounted、updated、destroyed 四個最常用的生命周期鉤子,說明它們的執行順序、典型用法,以及在實務開發中應避免的常見陷阱,並提供多個可直接套用的範例,幫助你從「概念」走向「實作」。


核心概念

1. beforeCreate

beforeCreate 是元件實例 建立 後、資料觀測 (reactivity)事件/監聽器 尚未設定前的第一個 Hook。此階段 無法 直接存取 datacomputedprops,但可以做以下事情:

  • 設定全域變數或外部服務(如 API 客戶端)。
  • 初始化非響應式屬性(例如純粹的暫存物件)。
  • 注入自訂插件或 mixin
export default {
  name: 'DemoBeforeCreate',
  beforeCreate() {
    // 只能使用 this.$options 取得元件設定
    console.log('Component is being created, options:', this.$options)

    // 初始化一個非響應式屬性
    this._timer = null   // 會在其他 hook 中使用
  },
  data() {
    return {
      message: 'Hello Vue!'
    }
  }
}

Tip:若需要在 data 初始化前就取得某些資訊(例如從 localStorage 讀取設定),可在 beforeCreate 完成,再於 data 中使用。


2. mounted

mounted虛擬 DOM 渲染完成、真實 DOM 插入 到文件後呼叫。此時 this.$el 已可取得,適合執行:

  • DOM 操作(如聚焦、測量尺寸)。
  • 第三方 UI 套件初始化(如 Chart.js、Swiper)。
  • 發送第一次 API 請求(若不需要在 created 中就發送)。
export default {
  name: 'DemoMounted',
  data() {
    return {
      users: []
    }
  },
  mounted() {
    // 1. 聚焦輸入框
    this.$refs.searchInput.focus()

    // 2. 取得容器寬度,傳給子元件
    const width = this.$el.clientWidth
    this.$emit('containerWidth', width)

    // 3. 發送 API 請求取得資料
    fetch('https://api.example.com/users')
      .then(res => res.json())
      .then(data => {
        this.users = data
      })
  },
  template: `
    <div>
      <input ref="searchInput" placeholder="搜尋使用者"/>
      <ul>
        <li v-for="u in users" :key="u.id">{{ u.name }}</li>
      </ul>
    </div>
  `
}

注意:若在 mounted 中執行大量計算,可能會阻塞 UI,建議使用 requestAnimationFramesetTimeout 延後執行。


3. updated

updated 會在 響應式資料變更、重新渲染完成 後觸發。常見用途:

  • 監控特定資料變化(如表格高度改變)。
  • 執行副作用(如滾動位置、動畫)。
  • 避免無限迴圈:若在 updated 中直接改變會觸發再次渲染,必須加條件判斷。
export default {
  name: 'DemoUpdated',
  data() {
    return {
      items: [],
      page: 1,
      loading: false
    }
  },
  methods: {
    loadMore() {
      if (this.loading) return
      this.loading = true
      fetch(`https://api.example.com/items?page=${this.page}`)
        .then(r => r.json())
        .then(res => {
          this.items = [...this.items, ...res.items]
          this.page++
          this.loading = false
        })
    }
  },
  updated() {
    // 只在 items 增加時自動捲動到底部
    this.$nextTick(() => {
      const container = this.$refs.list
      container.scrollTop = container.scrollHeight
    })
  },
  template: `
    <div ref="list" style="height:300px; overflow:auto;">
      <div v-for="i in items" :key="i.id">{{ i.title }}</div>
      <button @click="loadMore" :disabled="loading">載入更多</button>
    </div>
  `
}

技巧:使用 this.$nextTick 可保證 DOM 已更新,避免取得舊的高度或位置。


4. destroyed(Vue3 改名為 unmounted)

在 Vue3 中,destroyedunmounted 取代,但概念相同:元件即將從 DOM 移除,清理工作 必須在此完成。

  • 移除事件監聽(window、document、WebSocket)。
  • 銷毀第三方套件實例(如圖表、地圖)。
  • 清除計時器或非同步請求
export default {
  name: 'DemoUnmounted',
  data() {
    return {
      timerId: null,
      chart: null
    }
  },
  mounted() {
    // 設置自動更新的 timer
    this.timerId = setInterval(() => {
      this.fetchData()
    }, 5000)

    // 初始化 Chart.js
    const ctx = this.$refs.canvas.getContext('2d')
    this.chart = new Chart(ctx, {
      type: 'line',
      data: {/*...*/},
      options: {/*...*/}
    })
  },
  methods: {
    fetchData() {
      // 取得最新資料並更新 chart
      // ...
    }
  },
  unmounted() {   // Vue3 使用 unmounted
    // 清除 timer
    clearInterval(this.timerId)

    // 銷毀圖表
    if (this.chart) {
      this.chart.destroy()
    }

    // 移除全域監聽
    window.removeEventListener('resize', this.handleResize)
  },
  template: `<canvas ref="canvas"></canvas>`
}

提醒:若在 mounted 中使用 addEventListener,一定要在 unmounted 中對稱地 removeEventListener,避免記憶體洩漏。


常見陷阱與最佳實踐

陷阱 說明 解決方案
beforeCreate 讀取 this.data 此時資料尚未被觀測,會得到 undefined 只在 created 或之後使用 data
mounted 直接改變大量資料 會觸發一次以上的重新渲染,降低效能。 使用 this.$nextTick 或分批更新。
updated 中無條件改變資料 會造成 無限迴圈,瀏覽器卡死。 加上條件判斷或使用 watch 監聽更精確。
忘記在 unmounted 清除計時器 計時器仍在背景執行,導致記憶體泄漏。 unmountedclearInterval/clearTimeout
把 API 請求寫在 mounted 若元件在路由切換時快速卸載,請求仍在執行。 created 中發起請求,或在 unmounted 取消 pending 的 Promise (使用 AbortController)。

最佳實踐

  1. 資料取得盡量放在 created,因為此時已完成響應式設定,且不必等到 DOM 完成掛載。
  2. DOM 操作僅限 mounted,確保 this.$el 可用。
  3. 使用 watch 取代 updated,當只關心特定變數時,watch 更具可讀性且不易陷入迴圈。
  4. unmounted 中釋放所有外部資源,包括自訂事件、WebSocket、第三方元件等。
  5. 避免在生命周期內寫過長的程式,可抽離成方法或自訂 composable(即使仍使用 Options API,也可共用邏輯)。

實際應用場景

1. 表單自動聚焦與驗證

在使用者點擊「新增」按鈕時,彈出表單元件。透過 mounted 聚焦第一個輸入框,beforeCreate 初始化驗證規則,unmounted 清除驗證器。

2. 即時圖表與資料串流

一個儀表板元件在 mounted 建立 WebSocket 連線,收到資料後更新圖表;unmounted 時斷開連線並銷毀圖表實例,避免持續接收已不再顯示的資料。

3. 無限捲動清單

使用 mounted 監聽 scroll 事件,updated 判斷是否已載入新資料並自動捲到最底部,unmounted 移除 scroll 監聽,避免多個清單同時觸發。

4. 動態 SEO 元資訊

created 取得頁面資料,mounted 再利用 document.titlemeta 標籤更新 SEO 資訊,unmounted 時恢復原始標題,確保瀏覽器歷史紀錄正確。


總結

Vue3 的 Options API 仍是許多開發者的首選寫法,而 生命周期 Hook 則是掌控元件行為的關鍵。

  • beforeCreate 用於非響應式的初始化;
  • mounted 是操作 真實 DOM、啟動外部套件的最佳時機;
  • updated 讓你在資料變更後執行副作用,但要小心避免無限迴圈;
  • unmounted(舊稱 destroyed)則負責資源釋放,防止記憶體泄漏。

透過本文的概念說明與實作範例,你應該已能在專案中正確、有效地運用這四個 Hook,並結合 最佳實踐 來提升程式碼的可讀性與效能。未來若需要更彈性的邏輯或跨元件共享,仍可逐步探索 Composition API;但掌握 Options API 的生命周期,仍是 Vue 開發者不可或缺的基礎功。祝你在 Vue3 的旅程中寫出更乾淨、更可靠的程式碼!