在Dayxx
中提到過,父子組件之間的數據傳遞可以通過props
來實現。然而,當祖孫層組件需要進行數據傳遞時,中間層級的組件可能並不需要這個props
,但卻不得不為了使子組件獲取數據而被迫傳遞。
為了解決這個逐級透傳
的問題,我們可以使用provide(依賴提供者)
和inject(依賴注入者)
,這樣可以讓祖先組件直接提供數據給深層的子組件,避免不必要的props
傳遞。
使用inject
注入的深層組件可以讀取祖先組件透過provide
提供的值。這種傳遞方式與props
類似,同樣可以在深層組件中進行定義。
以下範例展示了祖先組件、中間組件和深層組件之間的關係,並演示了如何通過祖先組件傳值(message):
👉 Vue3 Options API 依賴灌注 - Provide & Inject 實作連結
祖先組件 Vue Template:
<div id="app">
<child-component></child-component>
</div>
中間層組件 Vue Template:
<template id="childComponent">
<div class="container child">
<h2>我是 childComponent</h2>
<deep-child-component></deep-child-component>
</div>
</template>
深層組件 Vue Template:
<template id="deepChildComponent">
<div class="container deepChild">
<h2>我是 deepChildComponent</h2>
<p> message:{{ message }}</p>
</div>
</template>
祖先組件 javascript:
const rootComponent = {
provide: { message: "hello world Vue!" },
components: { childComponent }
};
深層組件 javascript:
const deepChildComponent = {
inject: ["message"],
template: "#deepChildComponent"
};
但需要注意,如果中間層和祖先層同時使用相同名稱的provide
提供變數,深層組件讀取到的會是由中間層provide
提供的變數。因此,應謹慎管理 provide
的層級,以避免混淆和不必要的覆蓋。
👉 Vue3 Options API 依賴灌注 - Provide & Inject (中間層 provide 覆蓋)實作連結
將上面案例中的中間層修改一下:
中間層 javascript:
const childComponent = {
template: "#childComponent",
provide: { message: "hello world React!" },
components: { deepChildComponent }
};
會發現深層的deepChildComponent
組件inject
的值,被中間層替換到。
在子組件的inject
配置中,可以使用對象來定義from
和default
屬性,以設置依賴注入。
基礎類型
數據,可以直接提供預設值
;若是對象
類型,則應使用工廠函數
來確保每個組件實例都有獨立的數據。👉 Vue3 Options API 依賴灌注 - Inject 選項實作連結
祖先組件 javascript:
const rootComponent = {
provide: { message: "hello world Vue!" },
components: { childComponent }
};
深層組件 javascript:
const deepChildComponent = {
inject: {
newMessageName: {
from: "message",
}
},
template: "#deepChildComponent"
};
深層組件 Vue Template:
<template id="deepChildComponent">
<div class="container deepChild">
<h2>我是 deepChildComponent</h2>
<p> message:{{ newMessageName }}</p>
</div>
</template>
如果inject
注入的深層組件找不到對應的provide
值,Vue
會在開發環境中顯示警告。
這時候可以搭配default
設置inject
的預設值:
深層組件 javascrpit:
const deepChildComponent = {
inject: {
newMessageName: {
from: "message",
default: "我是預設值"
}
},
template: "#deepChildComponent"
};
默認情況下,Provide-Inject
使用對象定義時,會在組件初始化階段執行,因此無法在這個階段獲取this
的上下文資訊。如果需要獲取this
上下文的資訊,必須通過函數
的方式定義provide
,這樣每次被讀取時都會執行該函數,從而回傳最新的值。
👉 Vue3 Options API 依賴灌注 - Provide & Inject 函數讀取 this 初始值實作連結
以下是一個祖先組件定義計數器的案例,其中使用data
中的數據作為預設值,並將其提供給深層子組件使用:
祖先組件 Vue Template:
<div id="app">
<button @click="add" type="button">+ 1</button>
<h2>祖先組件顯示當前 count 變數值:{{ this.count }}</h2>
<child-component></child-component>
</div>
祖先組件 javascript:
const rootComponent = {
provide() {
return { message: `告訴你計數器數字:${this.count}` };
},
data() {
return {
count: 10
};
},
methods: {
add() {
this.count += 1;
}
},
components: { childComponent }
}
深層組件 Vue Template:
<template id="deepChildComponent">
<div class="container deepChild">
<h2>我是 deepChildComponent</h2>
<p> message:{{ message }}</p>
</div>
</template>
深層組件 javascript:
const deepChildComponent = {
inject: ["message"],
template: "#deepChildComponent"
};
data
中的數據在透過provide/inject
傳遞時,預設並不會建立響應式連結。因此,即使父組件透過方法更新了data
中的變數,使用inject
的子組件也不會自動同步更新。
如果需要讓被inject
的子組件與祖先組件中的data
中的變數保持響應式連結,可以使用computed 函數
來實現(這在Composition API
中經常使用)。
👉 Vue3 Options API 依賴灌注 - Provide & Inject 函數讀取 this 初始值並使用計算屬性實作連結
祖先組件 javascript:
const rootComponent = {
data() {
return {
count: 10
};
},
methods: {
add() {
this.count += 1;
}
},
provide() {
return {
message: computed(() => this.count * 2)
};
},
components: { childComponent }
};
provide
和inject
能夠有效解決props
的逐級傳遞問題,允許祖先組件直接與深層子組件共享數據,避免中間組件被迫透傳不需要的數據。當多個層級的組件提供了同名的 provide
值時,注入的組件會優先使用最近的provide
。provide
中提供的數據不是響應式的。如果需要保持數據的響應性,可以利用computed()
計算屬性函數來提供響應式的值。inject
時,可以通過from
修改注入依賴的名稱,並通過default
設定當找不到對應provide
時的預設值。對於物件類型,建議使用工廠函數,以確保每個組件實例都擁有獨立的數據。