背景
通常,當我們需要從父組件向子組件傳遞資料時,我們使用 props,想象一下這樣的結構:有一些深度嵌套的組件,而深層的子組件只需要父組件的部分內容,在這種情況下,如果仍然將 prop 沿著組件鏈逐級傳遞下去,可能會很麻煩,
對于這種情況,我們可以使用一對 provide 和 inject,無論組件層次結構有多深,父組件都可以作為其所有子組件的依賴提供者,這個特性有兩個部分:父組件有一個 provide 選項來提供資料,子組件有一個 inject 選項來開始使用這些資料,

使用
假設有一個組件A,A組件引入B組件(A為B的父組件) ,B組件引入C組件(B為C的父組件),即A為C的祖先組件,此時二者可以使用provide / inject進行通信,
舉例
A.vue
<template>
<div>
<B></B>
</div>
</template>
<script>
import B from "./B.vue";
export default {
name: "A",
components: {
B
},
};
</script>
B.vue
<template>
<div>
<C></C>
</div>
</template>
<script>
import C from "./C.vue";
export default {
name: "B",
components: {
C
},
};
</script>
C.vue
<template>
<div>
</div>
</template>
<script>
export default {
name: "C"
};
</script>
A與C使用provide / inject方式進行通信
A使用provide
<template>
<div>
<B></B>
</div>
</template>
<script>
import B from "./B.vue";
export default {
name: "A",
components: {
B
},
provide:{
name:"leo"
}
};
</script>
C使用inject
<template>
<div>
<span>{{name}}</span>
</div>
</template>
<script>
export default {
name: "C",
inject:["name"]
};
</script>
此時,C已經拿到A中的對應的name,但是,我們可能希望:當A中的name是本身某個可變化的資料時,如下:
<template>
<div>
<B></B>
</div>
</template>
<script>
import B from "./B.vue";
export default {
name: "A",
components: {
B
},
provide:{
name:this.name
},
data(){
return {
name:"leo"
}
},
methods:{
changeName(){
this.name = "lion"
}
}
};
</script>
我們希望當name改變時(如觸發changeName方法),對應的C中的name也要相應改變,但是使用以上方式時,C中的name并未隨著改變,此時需要我們進一步處理,即處理回應性,
處理回應性
在上面的例子中,如果我們更改了name,這個變化并不會反映在 inject 的 name property 中,這是因為默認情況下,provide/inject 系結并不是回應式的,在vue3中,我們可以通過傳遞一個 ref property 或 reactive 物件給 provide 來改變這種行為(下面展開),在我們的例子(vue2)中,如果我們想對祖先組件中的更改做出回應,我們需要將 provide 傳值進行改變,
A使用provide,此時傳入的應是一個回應式物件(如以下的obj)
<template>
<div>
<B></B>
</div>
</template>
<script>
import B from "./B.vue";
export default {
name: "A",
components: {
B
},
provide(){
return {
obj:this.obj //傳入一個回應式物件
}
},
data(){
return {
obj:{
name:"leo"
}
}
},
methods:{
changeName(){
this.obj.name = "lion"
}
}
};
</script>
C使用inject
<template>
<div>
<span>{{obj.name}}</span>
</div>
</template>
<script>
export default {
name: "C",
inject:["obj"] //接收回應式物件
};
</script>
此時A中的name改變,C中的值也會相應跟著變化,
以上為A向C傳資料,如果C向A傳資料(或者說C需要改變A中的資料),該如何做?
我們這里不讓C直接改變A中的資料,而是將A改變資料的方法通過provide傳給C,C執行該方法,觸發改變A中的資料,
A使用provide傳入一個方法
<template>
<div>
<span>{{obj.name}}</span>
<B></B>
</div>
</template>
<script>
import B from "./B.vue";
export default {
name: "A",
components: {
B
},
provide(){
return {
changeVal:this.changeName //傳入一個方法
}
},
data(){
return {
obj:{
name:"leo"
}
}
},
methods:{
changeName(val){ //C中觸發該方法執行,此時變成"lion"
this.obj.name = val
}
}
};
</script>
C使用inject
<template>
<div>
<span @click="changeName">點擊改變A組件資料</span>
</div>
</template>
<script>
export default {
name: "C",
inject:["changeVal"], //接收一個方法
methods:{
changeName(){
this.changeVal("lion") //執行此方法,改變A中的資料
}
}
};
</script>
以上就是在vue2中對provide / inject的基本使用,
vue3中使用
Provide
在 setup() 中使用 provide 時,我們首先從 vue 顯式匯入 provide 方法,這使我們能夠呼叫 provide 來定義每個 property,
provide 函式允許你通過兩個引數定義 property:
- name (
<String>型別) - value
使用A組件,provide 的值可以按如下方式重構:
<template>
<C />
</template>
<script>
import { provide } from 'vue'
import C from './C.vue'
export default {
components: {
C
},
setup() {
provide('location', 'North Pole')
provide('geolocation', {
longitude: 90,
latitude: 135
})
}
}
</script>
Inject
在 setup() 中使用 inject 時,也需要從 vue 顯式匯入,匯入以后,我們就可以呼叫它來定義暴露給我們的組件方式,
inject 函式有兩個引數:
- 要 inject 的 property 的 name
- 默認值 (可選)
使用C組件,可以使用以下代碼對其進行重構:
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
return {
userLocation,
userGeolocation
}
}
}
</script>
上面提到,在vue3中處理回應性的方式
為了增加 provide 值和 inject 值之間的回應性,我們可以在 provide 值時使用 ref 或 reactive,
<template>
<C />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import C from './C.vue'
export default {
components: {
C
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
}
}
</script>
現在,如果這兩個 property 中有任何更改,C組件也將自動更新!
同樣的,需要在C中修改它的祖先組件的資料,需像vue2一樣在provide傳入一個方法
<template>
<C />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import C from './C.vue'
export default {
components: {
C
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation) //傳入一個方法
}
}
</script>
C中使用inject
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
const updateUserLocation = inject('updateLocation')
return {
userLocation,
userGeolocation,
updateUserLocation //執行該方法,觸發祖先組件方法執行,從而改變資料
}
}
}
</script>
最后,如果要確保通過 provide 傳遞的資料不會被 inject 的組件更改,我們建議對提供者的 property 使用 readonly,
<template>
<C />
</template>
<script>
import { provide, reactive, readonly, ref } from 'vue'
import C from './C.vue'
export default {
components: {
C
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
// 使用readonly,資料只讀
provide('location', readonly(location))
provide('geolocation', readonly(geolocation))
provide('updateLocation', updateLocation)
}
}
</script>
總結:主要介紹了provide / inject 的基本使用以及在vue2、vue3中使用的區別,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/390393.html
標籤:其他
