Vue3全局組件通信之provide?/?inject詳解
1、前言
顧名思義,爺孫組件是比 父子組件通信 要更深層次的引用關(guān)系(也有稱之為 “隔代組件”):
C組件引入到B組件里,B組件引入到A組件里渲染,此時(shí)A是C的爺爺級(jí)別(可能還有更多層級(jí)關(guān)系),如果你用 props ,只能一級(jí)一級(jí)傳遞下去,那就太繁瑣了,因此我們需要更直接的通信方式。
他們之間的關(guān)系如下,Grandson.vue 并非直接掛載在 Grandfather.vue 下面,他們之間還隔著至少一個(gè) Son.vue (可能有多個(gè)):
Grandfather.vue └─Son.vue └─Grandson.vue
??????
因?yàn)樯舷录?jí)的關(guān)系的一致性,爺孫組件通信的方案也適用于 父子組件通信 ,只需要把爺孫關(guān)系換成父子關(guān)系即可。
2、provide / inject
這個(gè)特性有兩個(gè)部分:Grandfather.vue 有一個(gè) provide 選項(xiàng)來(lái)提供數(shù)據(jù),Grandson.vue 有一個(gè) inject 選項(xiàng)來(lái)開(kāi)始使用這些數(shù)據(jù)。
- Grandfather.vue 通過(guò) provide 向 Grandson.vue 傳值(可包含定義好的函數(shù))
- Grandson.vue 通過(guò) inject 向 Grandfather.vue 觸發(fā)爺爺組件的事件執(zhí)行
??????
無(wú)論組件層次結(jié)構(gòu)有多深,發(fā)起 provide 的組件都可以作為其所有下級(jí)組件的依賴提供者
這一部分的內(nèi)容變化都特別大,但使用起來(lái)其實(shí)也很簡(jiǎn)單,不用慌,也有相同的地方:
- 父組件不需要知道哪些子組件使用它 provide 的 property
- 子組件不需要知道 inject property 來(lái)自哪里
另外,要切記一點(diǎn)就是:provide 和 inject 綁定并不是可響應(yīng)的。這是刻意為之的,但如果傳入了一個(gè)可監(jiān)聽(tīng)的對(duì)象,那么其對(duì)象的 property 還是可響應(yīng)的。
3、發(fā)起 provide
我們先來(lái)回顧一下 2.x 的用法:
export default {
// 定義好數(shù)據(jù)
data () {
return {
tags: [ '中餐', '粵菜', '燒臘' ]
}
},
// provide出去
provide () {
return {
tags: this.tags
}
}
}舊版的 provide 用法和 data 類似,都是配置為一個(gè)返回對(duì)象的函數(shù)。
3.x 的新版 provide, 和 2.x 的用法區(qū)別比較大。
??????
在 3.x , provide 需要導(dǎo)入并在 setup 里啟用,并且現(xiàn)在是一個(gè)全新的方法。
每次要 provide 一個(gè)數(shù)據(jù)的時(shí)候,就要單獨(dú)調(diào)用一次。
每次調(diào)用的時(shí)候,都需要傳入 2 個(gè)參數(shù):
| 參數(shù) | 類型 | 說(shuō)明 |
|---|---|---|
| key | string | 數(shù)據(jù)的名稱 |
| value | any | 數(shù)據(jù)的值 |
來(lái)看一下如何創(chuàng)建一個(gè) provide:
// 記得導(dǎo)入provide
import { defineComponent, provide } from 'vue'
export default defineComponent({
// ...
setup () {
// 定義好數(shù)據(jù)
const msg: string = 'Hello World!';
// provide出去
provide('msg', msg);
}
})操作非常簡(jiǎn)單對(duì)吧,但需要注意的是,provide 不是響應(yīng)式的,如果你要使其具備響應(yīng)性,你需要傳入響應(yīng)式數(shù)據(jù)
4、接收 inject
也是先來(lái)回顧一下 2.x 的用法:
export default {
inject: ['tags'],
mounted () {
console.log(this.tags);
}
}舊版的 inject 用法和 props 類似,3.x 的新版 inject, 和 2.x 的用法區(qū)別也是比較大。
??????
在 3.x, inject 和 provide 一樣,也是需要先導(dǎo)入然后在 setup 里啟用,也是一個(gè)全新的方法。
每次要 inject 一個(gè)數(shù)據(jù)的時(shí)候,就要單獨(dú)調(diào)用一次。
每次調(diào)用的時(shí)候,只需要傳入 1 個(gè)參數(shù):
| 參數(shù) | 類型 | 說(shuō)明 |
|---|---|---|
| key | string | 與provide對(duì)應(yīng)的數(shù)據(jù)名稱 |
// 記得導(dǎo)入inject
import { defineComponent, inject } from 'vue'
export default defineComponent({
// ...
setup () {
const msg: string = inject('msg') || '';
}
})也是很簡(jiǎn)單(寫 TS 的話,由于 inject 到的值可能是 undefined,所以要么加個(gè) undefined 類型,要么給變量設(shè)置一個(gè)空的默認(rèn)值)。
5、響應(yīng)性數(shù)據(jù)的傳遞與接收
之所以要單獨(dú)拿出來(lái)說(shuō), 是因?yàn)樽兓娴暮艽?/p>
在前面我們已經(jīng)知道,provide 和 inject 本身不可響應(yīng),但是并非完全不能夠拿到響應(yīng)的結(jié)果,只需要我們傳入的數(shù)據(jù)具備響應(yīng)性,它依然能夠提供響應(yīng)支持。
我們以 ref 和 reactive 為例,來(lái)看看應(yīng)該怎么發(fā)起 provide 和接收 inject。
先在 Grandfather.vue 里 provide 數(shù)據(jù):
export default defineComponent({
// ...
setup () {
// provide一個(gè)ref
const msg = ref<string>('Hello World!');
provide('msg', msg);
// provide一個(gè)reactive
const userInfo: Member = reactive({
id: 1,
name: 'Petter'
});
provide('userInfo', userInfo);
// 2s 后更新數(shù)據(jù)
setTimeout(() => {
// 修改消息內(nèi)容
msg.value = 'Hi World!';
// 修改用戶名
userInfo.name = 'Tom';
}, 2000);
}
})
在 Grandsun.vue 里 inject 拿到數(shù)據(jù):
export default defineComponent({
setup () {
// 獲取數(shù)據(jù)
const msg = inject('msg');
const userInfo = inject('userInfo');
// 打印剛剛拿到的數(shù)據(jù)
console.log(msg);
console.log(userInfo);
// 因?yàn)?2s 后數(shù)據(jù)會(huì)變,我們 3s 后再看下,可以爭(zhēng)取拿到新的數(shù)據(jù)
setTimeout(() => {
console.log(msg);
console.log(userInfo);
}, 3000);
// 響應(yīng)式數(shù)據(jù)還可以直接給 template 使用,會(huì)實(shí)時(shí)更新
return {
msg,
userInfo
}
}
})非常簡(jiǎn)單,非常方便?。?!
??????
響應(yīng)式的數(shù)據(jù) provide 出去,在子孫組件拿到的也是響應(yīng)式的,并且可以如同自身定義的響應(yīng)式變量一樣,直接 return 給 template 使用,一旦數(shù)據(jù)有變化,視圖也會(huì)立即更新。
但上面這句話有效的前提是,不破壞數(shù)據(jù)的響應(yīng)性,比如 ref 變量,你需要完整的傳入,而不能只傳入它的 value,對(duì)于 reactive 也是同理,不能直接解構(gòu)去破壞原本的響應(yīng)性。
切記!切記?。。?/p>
6、引用類型的傳遞與接收 (針對(duì)非響應(yīng)性數(shù)據(jù)的處理)
provide 和 inject 并不是可響應(yīng)的,這是官方的故意設(shè)計(jì),但是由于引用類型的特殊性,在子孫組件拿到了數(shù)據(jù)之后,他們的屬性還是可以正常的響應(yīng)變化。
先在 Grandfather.vue 里 provide 數(shù)據(jù):
export default defineComponent({
// ...
setup () {
// provide 一個(gè)數(shù)組
const tags: string[] = [ '中餐', '粵菜', '燒臘' ];
provide('tags', tags);
// provide 一個(gè)對(duì)象
const userInfo: Member = {
id: 1,
name: 'Petter'
};
provide('userInfo', userInfo);
// 2s 后更新數(shù)據(jù)
setTimeout(() => {
// 增加tags的長(zhǎng)度
tags.push('叉燒');
// 修改userInfo的屬性值
userInfo.name = 'Tom';
}, 2000);
}
})在 Grandsun.vue 里 inject 拿到數(shù)據(jù):
export default defineComponent({
setup () {
// 獲取數(shù)據(jù)
const tags: string[] = inject('tags') || [];
const userInfo: Member = inject('userInfo') || {
id: 0,
name: ''
};
// 打印剛剛拿到的數(shù)據(jù)
console.log(tags);
console.log(tags.length);
console.log(userInfo);
// 因?yàn)?2s 后數(shù)據(jù)會(huì)變,我們 3s 后再看下,能夠看到已經(jīng)是更新后的數(shù)據(jù)了
setTimeout(() => {
console.log(tags);
console.log(tags.length);
console.log(userInfo);
}, 3000);
}
})
export default defineComponent({
setup () {
// 獲取數(shù)據(jù)
const tags: string[] = inject('tags') || [];
const userInfo: Member = inject('userInfo') || {
id: 0,
name: ''
};
// 打印剛剛拿到的數(shù)據(jù)
console.log(tags);
console.log(tags.length);
console.log(userInfo);
// 因?yàn)?2s 后數(shù)據(jù)會(huì)變,我們 3s 后再看下,能夠看到已經(jīng)是更新后的數(shù)據(jù)了
setTimeout(() => {
console.log(tags);
console.log(tags.length);
console.log(userInfo);
}, 3000);
}
})
??????
引用類型的數(shù)據(jù),拿到后可以直接用,屬性的值更新后,子孫組件也會(huì)被更新。
但是?。。?strong>由于不具備真正的響應(yīng)性,return 給模板使用依然不會(huì)更新視圖,如果涉及到視圖的數(shù)據(jù),請(qǐng)依然使用 響應(yīng)式 API 。
7、基本類型的傳遞與接收 (針對(duì)非響應(yīng)性數(shù)據(jù)的處理)
基本數(shù)據(jù)類型被直接 provide 出去后,再怎么修改,都無(wú)法更新下去,子孫組件拿到的永遠(yuǎn)是第一次的那個(gè)值。
先在 Grandfather.vue 里 provide 數(shù)據(jù):
export default defineComponent({
// ...
setup () {
// provide 一個(gè)數(shù)組的長(zhǎng)度
const tags: string[] = [ '中餐', '粵菜', '燒臘' ];
provide('tagsCount', tags.length);
// provide 一個(gè)字符串
let name: string = 'Petter';
provide('name', name);
// 2s 后更新數(shù)據(jù)
setTimeout(() => {
// tagsCount 在 Grandson 那邊依然是 3
tags.push('叉燒');
// name 在 Grandson 那邊依然是 Petter
name = 'Tom';
}, 2000);
}
})在 Grandsun.vue 里 inject 拿到數(shù)據(jù):
export default defineComponent({
setup () {
// 獲取數(shù)據(jù)
const name: string = inject('name') || '';
const tagsCount: number = inject('tagsCount') || 0;
// 打印剛剛拿到的數(shù)據(jù)
console.log(name);
console.log(tagsCount);
// 因?yàn)?2s 后數(shù)據(jù)會(huì)變,我們 3s 后再看下
setTimeout(() => {
// 依然是 Petter
console.log(name);
// 依然是 3
console.log(tagsCount);
}, 3000);
}
})很失望,并沒(méi)有變化。
??????
那么是否一定要定義成響應(yīng)式數(shù)據(jù)或者引用類型數(shù)據(jù)呢?
當(dāng)然不是,我們?cè)?provide 的時(shí)候,也可以稍作修改,讓它能夠同步更新下去。
先在 Grandfather.vue 里 provide 數(shù)據(jù):
export default defineComponent({
// ...
setup () {
// provide 一個(gè)數(shù)組的長(zhǎng)度
const tags: string[] = [ '中餐', '粵菜', '燒臘' ];
provide('tagsCount', (): number => {
return tags.length;
});
// provide 字符串
let name: string = 'Petter';
provide('name', (): string => {
return name;
});
// 2s 后更新數(shù)據(jù)
setTimeout(() => {
// tagsCount 現(xiàn)在可以正常拿到 4 了
tags.push('叉燒');
// name 現(xiàn)在可以正常拿到 Tom 了
name = 'Tom';
}, 2000);
}
})
再來(lái) Grandsun.vue 里修改一下 inject 的方式,看看這次拿到的數(shù)據(jù):
export default defineComponent({
setup () {
// 獲取數(shù)據(jù)
const tagsCount: any = inject('tagsCount');
const name: any = inject('name');
// 打印剛剛拿到的數(shù)據(jù)
console.log(tagsCount());
console.log(name());
// 因?yàn)?2s 后數(shù)據(jù)會(huì)變,我們 3s 后再看下
setTimeout(() => {
// 現(xiàn)在可以正確得到 4
console.log(tagsCount());
// 現(xiàn)在可以正確得到 Tom
console.log(name());
}, 3000);
}
})這次可以正確拿到數(shù)據(jù)了,看出這2次的寫法有什么區(qū)別了嗎?
??????
基本數(shù)據(jù)類型,需要 provide 一個(gè)函數(shù),將其 return 出去給子孫組件用,這樣子孫組件每次拿到的數(shù)據(jù)才會(huì)是新的。
但由于不具備響應(yīng)性,所以子孫組件每次都需要重新通過(guò)執(zhí)行 inject 得到的函數(shù)才能拿到最新的數(shù)據(jù)。
按我個(gè)人習(xí)慣來(lái)說(shuō),使用起來(lái)挺別扭的,能不用就不用……
由于不具備真正的響應(yīng)性,return 給模板使用依然不會(huì)更新視圖,如果涉及到視圖的數(shù)據(jù),請(qǐng)依然使用 響應(yīng)式 API 。
到此這篇關(guān)于Vue3全局組件通信之provide / inject的文章就介紹到這了,更多相關(guān)Vue3全局組件通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue?調(diào)用攝像頭掃描條碼功能實(shí)現(xiàn)代碼
本文介紹了如何使用Vue.js和jsQR庫(kù)來(lái)實(shí)現(xiàn)調(diào)用攝像頭并掃描條碼的功能,通過(guò)安裝依賴、獲取攝像頭視頻流、解析條碼等步驟,實(shí)現(xiàn)了從開(kāi)始掃描到停止掃描的完整流程,同時(shí),還強(qiáng)調(diào)了瀏覽器兼容性、HTTPS環(huán)境、權(quán)限問(wèn)題和性能優(yōu)化的重要性,感興趣的朋友一起看看吧2025-03-03
Vue3中內(nèi)置組件Teleport的基本使用與典型案例
Teleport是一種能夠?qū)⑽覀兊哪0逡苿?dòng)到DOM中Vue app之外的其他位置的技術(shù),下面這篇文章主要給大家介紹了關(guān)于Vue3中內(nèi)置組件Teleport的基本使用與典型案例的相關(guān)資料,需要的朋友可以參考下2023-04-04
vant組件中 dialog的確認(rèn)按鈕的回調(diào)事件操作
這篇文章主要介紹了vant組件中 dialog的確認(rèn)按鈕的回調(diào)事件操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
基于vue2.0實(shí)現(xiàn)的級(jí)聯(lián)選擇器
這篇文章主要介紹了基于vue2.0實(shí)現(xiàn)的級(jí)聯(lián)選擇器,基于Vue的級(jí)聯(lián)選擇器,可以單項(xiàng),二級(jí), 三級(jí)級(jí)聯(lián),多級(jí)級(jí)聯(lián),有興趣可以了解一下2017-06-06
vue實(shí)現(xiàn)導(dǎo)出word文檔功能實(shí)例(含多張圖片)
項(xiàng)目需要導(dǎo)出word,于是乎又是查閱資料,然后自己寫,下面這篇文章主要給大家介紹了關(guān)于vue實(shí)現(xiàn)導(dǎo)出word文檔功能(含多張圖片)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09

