聊聊Vue中provide/inject的應(yīng)用詳解
眾所周知,在組件式開(kāi)發(fā)中,最大的痛點(diǎn)就在于組件之間的通信。在 Vue 中,Vue 提供了各種各樣的組件通信方式,從基礎(chǔ)的 props/$emit 到用于兄弟組件通信的 EventBus,再到用于全局?jǐn)?shù)據(jù)管理的 Vuex。
在這么多的組件通信方式中,provide/inject 顯得十分阿卡林(毫無(wú)存在感)。但是,其實(shí) provide/inject 也有它們的用武之地。今天,我們就來(lái)聊聊 Vue 中 provide/inject 的應(yīng)用。
何為 provide/inject
provide/inject 是 Vue 在 2.2.0 版本新增的 API,官網(wǎng)介紹如下:
這對(duì)選項(xiàng)需要一起使用,以允許一個(gè)祖先組件向其所有子孫后代注入一個(gè)依賴(lài),不論組件層次有多深,并在起上下游關(guān)系成立的時(shí)間里始終生效。如果你熟悉 React,這與 React 的上下文特性很相似。
官網(wǎng)的解釋很讓人疑惑,那我翻譯下這幾句話:
provide 可以在祖先組件中指定我們想要提供給后代組件的數(shù)據(jù)或方法,而在任何后代組件中,我們都可以使用 inject 來(lái)接收 provide 提供的數(shù)據(jù)或方法。
舉個(gè)官網(wǎng)的🌰:
// 父級(jí)組件提供 'foo' var Provider = { provide: { foo: 'bar' }, // ... } // 子組件注入 'foo' var Child = { inject: ['foo'], created () { console.log(this.foo) // => "bar" } // ... }
可以看到,父組件提供的 foo 變量被子組件成功接收并使用。
了解了 provide/inject 是什么后,我們?cè)賮?lái)使用使用 provide/inject。
使用 provide/inject 做全局狀態(tài)管理
在日常開(kāi)發(fā)中,我們經(jīng)常會(huì)使用 Vuex 做狀態(tài)管理,但是,我個(gè)人一直不喜歡使用 Vuex,原因在于 Vuex 為了保持狀態(tài)可被回溯追蹤,使用起來(lái)太過(guò)繁瑣;而我之前參與的項(xiàng)目,較少多人合作,這個(gè)功能對(duì)于我來(lái)說(shuō),意義不大,我僅僅只需要 Vuex 中提供全局狀態(tài)的功能。
那么,有沒(méi)有方便快捷的實(shí)現(xiàn)全局狀態(tài)的方法呢?當(dāng)然有,這就是 provide/inject 這個(gè)黑科技 API 的一種使用方法。
很多人也許會(huì)想到一種方式:在根組件中,傳入變量,然后在后代組件中使用即可。
// 根組件提供一個(gè)非響應(yīng)式變量給后代組件 export default { provide () { return { text: 'bar' } } } // 后代組件注入 'app' <template> <div>{{this.text}}</div> </template> <script> export default { inject: ['text'], created() { this.text = 'baz' // 在模板中,依然顯示 'bar' } } </script>
這個(gè)想法,說(shuō)對(duì)也對(duì),說(shuō)不對(duì)也不對(duì),原因在于 provide 的特殊性。
在官網(wǎng)文檔中關(guān)于 provide/inject 有這么一個(gè)提示:
提示:provide 和 inject 綁定并不是可響應(yīng)的。這是刻意為之的。然而,如果你傳入了一個(gè)可監(jiān)聽(tīng)的對(duì)象,那么其對(duì)象的屬性還是可響應(yīng)的。
也就是說(shuō),Vue 不會(huì)對(duì) provide 中的變量進(jìn)行響應(yīng)式處理。所以,要想 inject 接受的變量是響應(yīng)式的,provide 提供的變量本身就需要是響應(yīng)式的。
由于組件內(nèi)部的各種狀態(tài)就是可響應(yīng)的,所以我們直接在根組件中將組件本身注入 provide,此時(shí),我們可以在后代組件中任意訪問(wèn)根組件中的所有狀態(tài),根組件就成為了全局狀態(tài)的容器,仔細(xì)想想,是不是很像 React 中的 context 呢?
代碼如下:
// 根組件提供將自身提供給后代組件 export default { provide () { return { app: this } }, data () { return { text: 'bar' } } } // 后代組件注入 'app' <template> <div>{{this.app.text}}</div> </template> <script> export default { inject: ['app'], created() { this.app.text = 'baz' // 在模板中,顯示 'baz' } } </script>
也許有的同學(xué)會(huì)問(wèn):使用 $root 依然能夠取到根節(jié)點(diǎn),那么我們何必使用 provide/inject 呢?
在實(shí)際開(kāi)發(fā)中,一個(gè)項(xiàng)目常常有多人開(kāi)發(fā),每個(gè)人有可能需要不同的全局變量,如果所有人的全局變量都統(tǒng)一定義在根組件,
勢(shì)必會(huì)引起變量沖突等問(wèn)題。
使用 provide/inject 不同模塊的入口組件傳給各自的后代組件可以完美的解決該問(wèn)題。
慎用 provide/inject
既然 provide/inject 如此好用,那么,為什么 Vue 官方還要推薦我們使用 Vuex,而不是用原生的 API 呢?
我在前面提到過(guò),Vuex 和 provide/inject 最大的區(qū)別在于,Vuex 中的全局狀態(tài)的每次修改是可以追蹤回溯的,而 provide/inject 中變量的修改是無(wú)法控制的,換句話說(shuō),你不知道是哪個(gè)組件修改了這個(gè)全局狀態(tài)。
Vue 的設(shè)計(jì)理念借鑒了 React 中的單向數(shù)據(jù)流原則(雖然有 sync 這種破壞單向數(shù)據(jù)流的家伙),而 provide/inject 明顯破壞了單向數(shù)據(jù)流原則。試想,如果有多個(gè)后代組件同時(shí)依賴(lài)于一個(gè)祖先組件提供的狀態(tài),那么只要有一個(gè)組件修改了該狀態(tài),那么所有組件都會(huì)受到影響。這一方面增加了耦合度,另一方面,使得數(shù)據(jù)變化不可控。如果在多人協(xié)作開(kāi)發(fā)中,這將成為一個(gè)噩夢(mèng)。
在這里,我總結(jié)了兩條條使用 provide/inject 做全局狀態(tài)管理的原則:
- 多人協(xié)作時(shí),做好作用域隔離
- 盡量使用一次性數(shù)據(jù)作為全局狀態(tài)
看起來(lái),使用 provide/inject 做全局狀態(tài)管理好像很危險(xiǎn),那么有沒(méi)有 provide/inject 更好的使用方式呢?當(dāng)然有,那就是使用 provide/inject 編寫(xiě)組件。
使用 provide/inject 編寫(xiě)組件
使用 provide/inject 做組件開(kāi)發(fā),是 Vue 官方文檔中提倡的一種做法。
以我比較熟悉的 elementUI 來(lái)舉例:
在 elementUI 中有 Button(按鈕)組件,當(dāng)在 Form(表單)組件中使用時(shí),它的尺寸會(huì)同時(shí)受到外層的 FormItem 組件以及更外層的 Form 組件中的 size 屬性的影響。
如果是常規(guī)方案,我們可以通過(guò) props 從 Form 開(kāi)始,一層層往下傳遞屬性值??雌饋?lái)只需要傳遞傳遞兩層即可,還可以接受。但是,F(xiàn)orm 的下一層組件不一定是 FormItem,F(xiàn)ormItem 的下一層組件不一定是 Button,它們之間還可以嵌套其他組件,也就是說(shuō),層級(jí)關(guān)系不確定。如果使用 props,我們寫(xiě)的組件會(huì)出現(xiàn)強(qiáng)耦合的情況。
provide/inject 可以完美的解決這個(gè)問(wèn)題,只需要向后代注入組件本身(上下文),后代組件中可以無(wú)視層級(jí)任意訪問(wèn)祖先組件中的狀態(tài)。
部分源碼如下:
// Button 組件核心源碼 export default { name: 'ElButton', // 通過(guò) inject 獲取 elForm 以及 elFormItem 這兩個(gè)組件 inject: { elForm: { default: '' }, elFormItem: { default: '' } }, // ... computed: { _elFormItemSize() { return (this.elFormItem || {}).elFormItemSize; }, buttonSize() { return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size; }, //... }, // ... };
總結(jié)
其實(shí)在 Vue 的學(xué)習(xí)中,遵循著二八法則,我們常用的 20% 的 API 就能解決大部分日常問(wèn)題,剩余的 API 感覺(jué)用處不大。但是,抽點(diǎn)時(shí)間去了解那些冷門(mén)的 API,也許你能發(fā)現(xiàn)一些不一般的風(fēng)景,令你在解決一些問(wèn)題時(shí),事半功倍。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- vue3和vue2中mixins的使用解析
- vue中使用mixins/extends傳入?yún)?shù)的方式
- 淺談Vue3 Composition API如何替換Vue Mixins
- vue2中provide/inject的使用與響應(yīng)式傳值詳解
- Vue.js中provide/inject實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)更新的方法示例
- vue中使用[provide/inject]實(shí)現(xiàn)頁(yè)面reload的方法
- vue中的provide/inject的學(xué)習(xí)使用
- Vue中的 mixins 和 provide/inject詳解
相關(guān)文章
前端使用el-table自帶排序功能\后端排序方法實(shí)例
在Vue.js中使用Element UI庫(kù)時(shí)可以通過(guò)el-table組件來(lái)展示表格數(shù)據(jù),并支持列排序,下面這篇文章主要給大家介紹了關(guān)于前端使用el-table自帶排序功能\后端排序的相關(guān)資料,需要的朋友可以參考下2024-08-08詳解如何在Vue項(xiàng)目中發(fā)送jsonp請(qǐng)求
這篇文章主要介紹了詳解如何在Vue項(xiàng)目中發(fā)送jsonp請(qǐng)求,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10vue2.0結(jié)合Element實(shí)現(xiàn)select動(dòng)態(tài)控制input禁用實(shí)例
本篇文章主要介紹了vue2.0結(jié)合Element實(shí)現(xiàn)select動(dòng)態(tài)控制input禁用實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05vue-cli3 取消eslint校驗(yàn)代碼的解決辦法
這篇文章主要介紹了vue-cli3 取消eslint校驗(yàn)代碼的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01vuepress實(shí)現(xiàn)自定義首頁(yè)的樣式風(fēng)格
這篇文章主要介紹了vuepress實(shí)現(xiàn)自定義首頁(yè)的樣式風(fēng)格,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08Vue3異步數(shù)據(jù)加載組件suspense的使用方法
前端開(kāi)發(fā)中異步請(qǐng)求是非常常見(jiàn)的事情,比如遠(yuǎn)程讀取圖片,調(diào)用后端接口等等,這篇文章主要給大家介紹了關(guān)于Vue3異步數(shù)據(jù)加載組件suspense的使用方法,suspense中文含義是懸念的意思,需要的朋友可以參考下2021-08-08vue-devtools 打開(kāi)源碼位置實(shí)現(xiàn)過(guò)程
這篇文章主要為大家介紹了vue-devtools 打開(kāi)源碼位置實(shí)現(xiàn)過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09