本文 AI 產出,尚未審核

Vue3 元件通信 – provide / inject

簡介

在大型 Vue 應用程式中,元件之間的資料傳遞往往不只局限於父子關係。provide / inject 讓我們可以在「祖先」元件一次性提供資料,讓任意深度的子孫元件直接取得,省去層層 props 轉遞或繁瑣的事件機制。

這套機制特別適合 全局設定、主題樣式、國際化、共享狀態 等情境。掌握它不僅能讓程式碼更乾淨,也能提升可維護性與重用性,是 Vue3 進階開發者必備的工具。


核心概念

1. 基本使用方式

provide 在祖先元件內宣告,inject 在子孫元件內取得。兩者以鍵值 (key) 為橋樑,鍵可以是字串或 Symbol(後者更安全)。

// Parent.vue
<script setup>
import { provide } from 'vue'

const theme = 'dark'               // 可以是任何資料型別
provide('appTheme', theme)         // 使用字串作為 key
</script>

<template>
  <slot></slot>                    <!-- 讓子元件插入 -->
</template>
// Child.vue
<script setup>
import { inject } from 'vue'

const theme = inject('appTheme')   // 直接取得祖先提供的值
</script>

<template>
  <div>目前主題:{{ theme }}</div>
</template>

注意inject 只能在 setup()script setup 中使用,不能在普通的 Options API 中直接呼叫。


2. 提供可響應的資料

若提供的值是 refreactive,子孫元件取得後仍會保持響應式。

// Provider.vue
<script setup>
import { ref, provide } from 'vue'

const count = ref(0)
provide('counter', count)          // 提供 ref
</script>

<template>
  <button @click="count++">+1</button>
  <slot></slot>
</template>
// Descendant.vue
<script setup>
import { inject } from 'vue'

const count = inject('counter')    // 取得同一個 ref
</script>

<template>
  <p>計數值:{{ count }}</p>       <!-- 會自動更新 -->
</template>

3. 使用 Symbol 作為鍵

字串鍵雖然直觀,但在大型專案中容易與其他元件衝突。使用 Symbol 可保證唯一性。

// token.js
export const ThemeSymbol = Symbol('appTheme')
// RootProvider.vue
<script setup>
import { provide } from 'vue'
import { ThemeSymbol } from './token.js'

provide(ThemeSymbol, { mode: 'light' })
</script>
// AnyChild.vue
<script setup>
import { inject } from 'vue'
import { ThemeSymbol } from './token.js'

const theme = inject(ThemeSymbol)
</script>

4. 提供函式(Factory)

有時候需要根據子元件的需求動態產生資料,這時可以提供 factory function

// ServiceProvider.vue
<script setup>
import { provide } from 'vue'

function createLogger(name) {
  return (msg) => console.log(`[${name}] ${msg}`)
}
provide('loggerFactory', createLogger)
</script>
// LoggerUser.vue
<script setup>
import { inject } from 'vue'

const loggerFactory = inject('loggerFactory')
const logger = loggerFactory('MyComponent')
logger('Hello world!')
</script>

5. 結合 Composition API 建立可重用的「provide」

provide 包裝成自訂 composable,讓多個元件只要呼叫一次即可完成設定。

// useThemeProvider.js
import { ref, provide } from 'vue'
export const ThemeKey = Symbol('theme')

export function useThemeProvider(initial = 'dark') {
  const mode = ref(initial)
  const toggle = () => (mode.value = mode.value === 'dark' ? 'light' : 'dark')
  provide(ThemeKey, { mode, toggle })
}
// App.vue
<script setup>
import { useThemeProvider } from './useThemeProvider.js'
useThemeProvider()
</script>

<template>
  <router-view />
</template>
// ThemeToggler.vue
<script setup>
import { inject } from 'vue'
import { ThemeKey } from './useThemeProvider.js'

const { mode, toggle } = inject(ThemeKey)
</script>

<template>
  <button @click="toggle">切換主題 ({{ mode }})</button>
</template>

常見陷阱與最佳實踐

陷阱 說明 解法
忘記提供預設值 inject 若找不到對應的 key,會回傳 undefined,導致渲染錯誤。 inject 時傳入第二個參數作為預設值:inject('key', defaultValue)
使用非響應式資料 提供普通物件後子元件不會自動更新。 若需要雙向更新,使用 ref / reactive 包裝;或提供 setter 函式。
鍵名衝突 多個模組使用相同字串鍵會互相覆寫。 使用 Symbol 或集中管理的 token 檔案。
過度依賴 把大量全局狀態都放在 provide / inject,會失去 Vuex / Pinia 的可追蹤優勢。 僅在「跨層級共享且不需要頻繁變更」的情境使用,較大的全局狀態仍建議使用 Pinia。
無法在 Options API 中直接使用 inject 必須在 setup 裡呼叫。 若仍想使用 Options API,可在 created 內透過 this.$.setupState 取得,或改寫成 mixins

最佳實踐

  1. 鍵名統一管理:建立 constants/tokens.js,全部使用 Symbol
  2. 只提供provide 的原則是 只提供不在子元件中改變(除非提供的是 ref)。
  3. 文件化:在專案文件中註明每個 provide 的目的、型別與預設值,方便團隊協作。
  4. 組合式封裝:將相關的 provide / inject 包裝成 composable,提升可測試性與重用性。

實際應用場景

  1. 主題 (Theme) 切換

    • 祖先元件提供當前主題與切換函式,所有子元件只要 inject 即可直接讀取與切換,避免每層都傳 prop
  2. 表單驗證規則

    • 在表單容器 FormProviderprovide 整體的驗證規則或錯誤訊息,子欄位 FormItem 只需要 inject 取得即可。
  3. 多語系 (i18n) 文字

    • 透過 provide 注入當前語系的翻譯函式 t(key),讓深層的 UI 元件不必一次次傳遞 locale
  4. 第三方插件的全局實例

    • axiossocket.ioMapbox 等,在根元件 provide 一個實例,子元件直接 inject 使用,保持單例。
  5. 動態表格欄位設定

    • 父層根據使用者權限 provide 欄位可見性與編輯權限,子層表格元件自動根據注入的設定渲染。

總結

provide / inject 是 Vue3 為了解決「跨層級」資料共享而設計的輕量級機制。

  • 透過 鍵值 連結祖先與子孫,省去層層傳遞
  • 使用 ref / reactive 可保持 響應式,配合 Symbol 確保唯一性。
  • 函式工廠Composable 包裝 讓它更彈性且易於重用。

在實務開發中,將 provide / inject 用於 主題、i18n、全局服務、表單規則 等「不頻繁變更」的共享資訊,能讓程式碼結構更清晰、維護成本更低。記得遵守 鍵名統一管理只提供不改變 的原則,並在需要更完整狀態管理時搭配 Pinia。掌握這套工具,你的 Vue3 專案將更具可擴充性與可讀性。