Skip to content

Vue 3 實戰:掌握 Provide / Inject 跨層級資料傳遞與響應式共享

Vue 3 跨層級傳遞:provide 與 inject

在開發大型 Vue 應用時,我們常會遇到元件嵌套層級過深的問題。若要將祖先元件的資料傳給深層的曾孫元件,傳統的 props 必須像接力賽一樣層層傳遞,這不僅費時、費工,更會導致元件間產生不必要的耦合。

provide / inject 機制正是為了打破這種困境,實現 依賴注入(Dependency Injection),讓資料能直接跨越層級進行通訊。

基本用法:打破層層傳遞的僵局

這套機制由兩部分組成:父層負責 提供 provide,子層負責 注入 inject

祖先元件:提供資料

在較外層的元件中,我們定義要分享出去的資料:

js
import { provide } from 'vue';

provide('theme', 'dark');

深層子元件:接收資料

無論中間隔了多少層,子元件都能直接取出資料:

js
import { inject } from 'vue';

const theme = inject('theme');

安全建議

如果沒有父元件的 provide 時,子元件使用 inject 要噴警告! 因此,建議加上預設值較佳。

js
const theme = inject('theme', 'light');

進階應用:共享響應式狀態

傳遞靜態字串固然方便,但在實戰中,我們更常需要共享可以被更新的 響應式資料ref() / reactive()

範例:全域計數器

當我們傳遞一個 ref() 時,子元件接收到的也會是具備響應性的物件。

父元件 provide

js
import { ref, provide } from 'vue';

const counter = ref(0);
provide('counter', counter);

子元件 inject

js
import { inject } from 'vue';

const counter = inject('counter', 10);

counter.value++;

範例:封裝更新邏輯

雖然子元件可以直接修改 inject 進來的值,但在大型專案中,為了追蹤數據變化,建議遵循 誰提供,誰修改 的原則:將修改資料的方法一併 provide 出去。

父元件 provide:提供資料與方法

js
import { provide } from 'vue';

function updateTheme(val) {
  theme.value = val;
}

provide('updateTheme', updateTheme);

子元件 inject:呼叫方法進行更新

js
import { inject } from 'vue';

const updateTheme = inject('updateTheme');

updateTheme('light');

總結:何時該使用 Provide / Inject?

  • 開發通用元件庫:Tabs、Accordion,父元件需要與多個不確定層級的子元件通訊。
  • 全域主題切換:深層 UI 元素需要讀取目前的主題色、語言設定。
  • 避免 Prop Drilling:當您的 Props 只是為了傳給下一個元件,自己卻完全沒用到時。

專業建議

雖然 provide / inject 很方便,但若資料流過於複雜,建議評估是否改用 Pinia 等狀態管理工具,以獲得更好的除錯支援與開發者工具(DevTools)追蹤。

進階:用 readonly 與 Symbol 讓共享更安全

js
import { provide, readonly, ref } from 'vue';

const theme = ref('dark');

// 後代只能讀取,想更新一律走 updateTheme,資料流更好追蹤
provide('theme', readonly(theme));
provide('updateTheme', (val) => (theme.value = val));

大型專案建議把 key 改成 Symbol 並集中匯出,避免字串重複命名:

js
// keys.js
export const THEME_KEY = Symbol('theme');

常見問題

什麼是 Prop Drilling?

為了把資料送到深層元件,中間每一層都得宣告並轉交同一個 prop,自己卻用不到。層級一多就會造成大量樣板程式碼與不必要的耦合。

provide 的資料是響應式的嗎?

取決於您提供什麼。提供 refreactive 物件時,後代拿到的就是具備響應性的資料;提供原始值則只是一份靜態快照,之後更新不會反映到後代。

inject 找不到對應的 provide 會怎麼樣?

會得到 undefined 並在開發模式下發出警告。建議在 inject 的第二個參數提供預設值,讓元件在缺少祖先時仍能運作。

後代可以直接修改注入的資料嗎?

技術上可以,但建議遵循 誰提供誰修改 的原則,把更新方法一併 provide 出去。也可以用 readonly 包裝資料,從根本上阻止後代直接改動。

provide / inject 與 Pinia 該怎麼選?

provideinject 適合元件樹內部的區域性共享,例如:通用元件庫的內部溝通;當狀態需要跨頁面、跨模組共用,或需要 DevTools 追蹤與除錯,就該改用 Pinia

注入的 key 可以避免命名衝突嗎?

可以。大型專案建議改用 Symbol 當作 key,並集中在同一個檔案匯出,既能避免字串重複,也方便統一管理。

延伸閱讀