想象一個場景,子組件依賴父組件傳遞的props,如果有多個這樣的props,其實已經(jīng)很煩了,當有多個子組件,或者孫組件也依賴于由上至下的props時,寫那么多props簡直是要了親命了。
為了解決這種麻煩,產生了vuex。但是我們肯定希望組件的高可復用性,不引入vuex就能實現(xiàn)功能,那就要考慮vue本身是否支持這種能力了。熟悉React開發(fā)的人應該會想到Context這個高階組件,它通過Provider和Consumer,可以使得無限嵌套的父子組件共有一個數(shù)據(jù)源。Vue也有一個類似的,連名字都很相似,就是這篇文章的主人公provide和inject。
依賴注入 ,官網(wǎng)有個例子,講述了為何我們需要 provide,它是可以終結復雜數(shù)據(jù)嵌套的神器。慣例先吹一波,不然寫這篇文章的意義沒了,=^^= 。閑話少說,上代碼。
// parent組件
<template>
<div>
<div>{{test}}</div>
<button @click="changeTest">修改test的值</button>
<son></son>
</div>
</template>
<script>
import Son from "./Son";
export default {
name: "parent",
components: { Son },
provide() {
return {
injectData: this.test
};
},
data() {
return {
test: "測試",
};
},
methods: {
changeTest() {
this.test = "測試后";
}
}
};
</script>
//son組件
<template>
<div>{{injectData}}</div>
</template>
<script>
export default {
name: "son",
inject: ["injectData"],
mounted() {
// eslint-disable-next-line
console.log(this.injectData)
},
};
</script>
parent組件使用provide提供一個injectData,son組件通過inject獲取到parent注入的數(shù)據(jù),以上就是它的最簡用法。有一點需要注意,我們可以將inject得到的數(shù)據(jù)直接賦值給子組件的data或props,但是這個是在vue版本2.1之后才有的功能,這版本之前,會在data,props得到之后再得到注入的值。
使用provide/inject我們需要注意的是:
provide/inject這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在起上下游關系成立的時間里始終生效。
provide 選項應該是:一個對象或返回一個對象的函數(shù)
inject 選項應該是:一個字符串數(shù)組,或 一個對象,對象的 key 是本地的綁定名
如果我們parent組件里provide是如下代碼:
provide: {
injectData: this.test
}
我們會發(fā)現(xiàn)this.test所得到的永遠是undefined
provide: {
injectData:() => {
//eslint-disable-next-line
console.log(this)
}
},
我們打印出來的this永遠是undefined,所以我們最好切記使用return返回一個對象,直接賦值對象僅在我們需要傳遞的是靜態(tài)數(shù)據(jù)時再使用。
最開始的例子測試中,我們會得到,provide傳遞的test數(shù)據(jù)并不是響應式的,當parent改變了test的值后,son組件無法得到改變后的值,這就產生了一個問題,我們肯定是希望父子組件拿到的數(shù)據(jù)是相同的。

我們先看看官網(wǎng)是如何說的,哦,并不直接是可響應的,而是要我們人為使它是可響應的,之前的測試是符合的,因為我們provide的只是一個字符串,可響應的我們很快想到對象,數(shù)組這些。這樣又產生一個問題,是provide傳遞的值就必須是個對象,還是說,傳遞的可以是個指針,指向的是個對象呢?實踐出真知,測試一下。
provide() {
return {
injectData: this.test
};
},
data() {
return {
test: {
test: "測試"
},
};
},
我們先將provide的改成上面的,結果是響應式的,我們再測試一下另外一種。
provide() {
return {
injectData: {
test: this.test
}
};
},
data() {
return {
test: "測試"
};
},
測試得出結果,上面的不是響應式的。事實證明,必須是inject能拿到的數(shù)據(jù)對象響應式,我們才能得到響應式結果。
其實這樣也蠻麻煩的,比如如果父組件有超多需要傳遞下去的數(shù)據(jù),還有個超簡潔的處理方式。
provide() {
return {
injectData: this
}
}
我們可以將父組件整個傳遞到所需組件中去,并且組件實例本身就是響應式的,這樣足以滿足我們大部分組件需求。
使用時,我這邊還是遇到了一種場景,不是provide的問題,但是與之有點關系。當我們將組件實例注入之后,子組件是能拿到組件的所有數(shù)據(jù)的,但是當父組件內里數(shù)據(jù)改變時,子組件是監(jiān)聽不到數(shù)據(jù)的變化的,頁面也無法刷新,我是取了個巧,對數(shù)組進行 splice或者concat等其他會觸發(fā)數(shù)組更新監(jiān)聽的操作,對象的話可以使用...或Object.assign。$set沒什么用 ==!響應式更新機制,還是有很多不便的地方,刷新的解決方式還有待商榷,希望有更好的方法。