這篇文章主要介紹了Vue3中Provide和Inject的實(shí)現(xiàn)原理是什么的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇Vue3中Provide和Inject的實(shí)現(xiàn)原理是什么文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。
創(chuàng)新互聯(lián)公司長(zhǎng)期為上1000+客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為普定企業(yè)提供專業(yè)的網(wǎng)站制作、做網(wǎng)站,普定網(wǎng)站改版等技術(shù)服務(wù)。擁有十載豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
prototype 與 __proto__
prototype 一般稱為顯式原型,__proto__
一般稱為隱式原型。 每一個(gè)函數(shù)在創(chuàng)建之后,在默認(rèn)情況下,會(huì)擁有一個(gè)名為 prototype 的屬性,這個(gè)屬性表示函數(shù)的原型對(duì)象。
原型鏈
當(dāng)我們?cè)L問(wèn)一個(gè)JS對(duì)象屬性的時(shí)候,JS先會(huì)在這個(gè)對(duì)象定義的屬性里找,找不到就會(huì)沿著這個(gè)對(duì)象的__proto__
這個(gè)隱式原型關(guān)聯(lián)起來(lái)的鏈條向上一個(gè)對(duì)象查找,這個(gè)鏈條就叫原型鏈。
function Fn() {} Fn.prototype.name = 'coboy' let fn1 = new Fn() fn1.age = 18 console.log(fn1.name) // coboy console.log(fn1.age) // 18
fn1是Fn函數(shù)new出來(lái)的實(shí)例對(duì)象,fn1.age是這個(gè)實(shí)例對(duì)象上屬性,fn1.name則從Fn.prototype原型對(duì)象而來(lái),因?yàn)閒n1的__proto__
隱式原型就是指向Fn這個(gè)函數(shù)的原型對(duì)象Fn.prototype。原型鏈某種意義上是讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。
function Fn() {} Fn.prototype.name = 'coboy' let fn1 = new Fn() fn1.name = 'cobyte' console.log(fn1.name) // cobyte
當(dāng)訪問(wèn)fn1這個(gè)實(shí)例對(duì)象的屬性name的時(shí)候,JS先會(huì)在fn1這個(gè)實(shí)例對(duì)象的屬性里查找,剛好fn1定義了一個(gè)name屬性,所以就直接返回自身屬性的值cobyte,否則就會(huì)繼續(xù)沿著原型鏈向Fn.prototype上去找,那么就會(huì)返回coboy。
復(fù)習(xí)完原型和原型鏈的知識(shí)之后,我們就開(kāi)始進(jìn)入Provide/Inject的實(shí)現(xiàn)原理探索。
在 setup()
中使用 provide
時(shí),我們首先從 vue
顯式導(dǎo)入 provide
方法。這使我們能夠調(diào)用 provide
來(lái)定義每個(gè) property。
provide
函數(shù)允許你通過(guò)兩個(gè)參數(shù)定義 property
name (<String>
類型)
value
import { provide } from 'vue' export default { setup() { provide('name', 'coboy') } }
那么這個(gè)provide API實(shí)現(xiàn)原理是什么呢?
provide 函數(shù)可以簡(jiǎn)化為
export function provide(key, value) { // 獲取當(dāng)前組件實(shí)例 const currentInstance: any = getCurrentInstance() if(currentInstance) { // 獲取當(dāng)前組件實(shí)例上provides屬性 let { provides } = currentInstance // 獲取當(dāng)前父級(jí)組件的provides屬性 const parentProvides = currentInstance.parent.provides // 如果當(dāng)前的provides和父級(jí)的provides相同則說(shuō)明還沒(méi)賦值 if(provides === parentProvides) { // Object.create() es6創(chuàng)建對(duì)象的另一種方式,可以理解為繼承一個(gè)對(duì)象, 添加的屬性是在原型下。 provides = currentInstance.provides = Object.create(parentProvides) } provides[key] = value } }
綜上所述provide API就是通過(guò)獲取當(dāng)前組件的實(shí)例對(duì)象,將傳進(jìn)來(lái)的數(shù)據(jù)存儲(chǔ)在當(dāng)前的組件實(shí)例對(duì)象上的provides上,并且通過(guò)ES6的新API Object.create把父組件的provides屬性設(shè)置到當(dāng)前的組件實(shí)例對(duì)象的provides屬性的原型對(duì)象上。
我們通過(guò)查看instance對(duì)象的源碼,可以看到,在instance組件實(shí)例對(duì)象上,存在parent和provides兩個(gè)屬性。在初始化的時(shí)候如果存在父組件則把父組件的provides賦值給當(dāng)前的組件實(shí)例對(duì)象的provides,如果沒(méi)有就創(chuàng)建一個(gè)新的對(duì)象,并且把應(yīng)用上下文的provides屬性設(shè)置為新對(duì)象的原型對(duì)象上的屬性。
在 setup()
中使用 inject
時(shí),也需要從 vue
顯式導(dǎo)入。導(dǎo)入以后,我們就可以調(diào)用它來(lái)定義暴露給我們的組件方式。
inject
函數(shù)有兩個(gè)參數(shù):
要 inject 的 property 的 name
默認(rèn)值 (可選)
import { inject } from 'vue' export default { setup() { const name = inject('name', 'cobyte') return { name } } }
那么這個(gè)inject API實(shí)現(xiàn)原理是什么呢?
inject 函數(shù)可以簡(jiǎn)化為
export function inject( key, defaultValue, treatDefaultAsFactory = false ) { // 獲取當(dāng)前組件實(shí)例對(duì)象 const instance = currentInstance || currentRenderingInstance if (instance) { // 如果intance位于根目錄下,則返回到appContext的provides,否則就返回父組件的provides const provides = instance.parent == null ? instance.vnode.appContext && instance.vnode.appContext.provides : instance.parent.provides if (provides && key in provides) { return provides[key] } else if (arguments.length > 1) { // 如果存在1個(gè)參數(shù)以上 return treatDefaultAsFactory && isFunction(defaultValue) // 如果默認(rèn)內(nèi)容是個(gè)函數(shù)的,就執(zhí)行并且通過(guò)call方法把組件實(shí)例的代理對(duì)象綁定到該函數(shù)的this上 ? defaultValue.call(instance.proxy) : defaultValue } } }
通過(guò)inject源碼分析我們可以知道,inject里面先獲取當(dāng)前組件的實(shí)例對(duì)象,然后判斷是否根組件,如果是根組件則返回到appContext的provides,否則就返回父組件的provides。
如果當(dāng)前獲取的key在provides上有值,那么就返回該值,如果沒(méi)有則判斷是否存在默認(rèn)內(nèi)容,默認(rèn)內(nèi)容如果是個(gè)函數(shù),就執(zhí)行并且通過(guò)call方法把組件實(shí)例的代理對(duì)象綁定到該函數(shù)的this上,否則就直接返回默認(rèn)內(nèi)容。
通過(guò)上面的分析,可以得知provide/inject實(shí)現(xiàn)原理還是比較簡(jiǎn)單的,就是巧妙地利用了原型和原型鏈進(jìn)行數(shù)據(jù)的繼承和獲取。provide API調(diào)用設(shè)置的時(shí)候,設(shè)置父級(jí)的provides為當(dāng)前provides對(duì)象原型對(duì)象上的屬性,在inject獲取provides對(duì)象中的屬性值時(shí),優(yōu)先獲取provides對(duì)象自身的屬性,如果自身查找不到,則沿著原型鏈向上一個(gè)對(duì)象中去查找。
方法說(shuō)明
Object.create()方法創(chuàng)建一個(gè)新的對(duì)象,并以方法的第一個(gè)參數(shù)作為新對(duì)象的__proto__
屬性的值(以第一個(gè)參數(shù)作為新對(duì)象的構(gòu)造函數(shù)的原型對(duì)象)
Object.create()方法還有第二個(gè)可選參數(shù),是一個(gè)對(duì)象,對(duì)象的每個(gè)屬性都會(huì)作為新對(duì)象的自身屬性,對(duì)象的屬性值以descriptor(Object.getOwnPropertyDescriptor(obj, 'key'))的形式出現(xiàn),且enumerable默認(rèn)為false
源碼模擬
Object.myCreate = function (proto, propertyObject = undefined) { if (propertyObject === null) { // 這里沒(méi)有判斷propertyObject是否是原始包裝對(duì)象 throw 'TypeError' } else { function Fn () {} // 設(shè)置原型對(duì)象屬性 Fn.prototype = proto const obj = new Fn() if (propertyObject !== undefined) { Object.defineProperties(obj, propertyObject) } if (proto === null) { // 創(chuàng)建一個(gè)沒(méi)有原型對(duì)象的對(duì)象,Object.create(null) obj.__proto__ = null } return obj } }
定義一個(gè)空的構(gòu)造函數(shù),然后指定構(gòu)造函數(shù)的原型對(duì)象,通過(guò)new運(yùn)算符創(chuàng)建一個(gè)空對(duì)象,如果發(fā)現(xiàn)傳遞了第二個(gè)參數(shù),通過(guò)Object.defineProperties為創(chuàng)建的對(duì)象設(shè)置key、value,最后返回創(chuàng)建的對(duì)象即可。
示例
// 第二個(gè)參數(shù)為null時(shí),拋出TypeError // const throwErr = Object.myCreate({name: 'coboy'}, null) // Uncaught TypeError // 構(gòu)建一個(gè)以 const obj1 = Object.myCreate({name: 'coboy'}) console.log(obj1) // {}, obj1的構(gòu)造函數(shù)的原型對(duì)象是{name: 'coboy'} const obj2 = Object.myCreate({name: 'coboy'}, { age: { value: 18, enumerable: true } }) console.log(obj2) // {age: 18}, obj2的構(gòu)造函數(shù)的原型對(duì)象是{name: 'coboy'}
provides = currentInstance.provides = Object.create(parentProvides)
發(fā)生了什么?
Object.create(parentProvides)
創(chuàng)建了一個(gè)新的對(duì)象引用,如果只是把 currentInstance.provides
更新為新的對(duì)象引用,那么provides
的引用還是舊的引用,所以需要同時(shí)把provides
的引用也更新為新的對(duì)象引用。
來(lái)自《JavaScript權(quán)威指南》的解析
JavaScript總是嚴(yán)格按照從左至右的順序來(lái)計(jì)算表達(dá)式
一切都是表達(dá)式,一切都是運(yùn)算
provides = currentInstance.provides = Object.create(parentProvides)
上述的provides是一個(gè)表達(dá)式,它被嚴(yán)格地稱為“賦值表達(dá)式的左手端(Ihs)操作數(shù)”。
而右側(cè) currentInstance.provides = Object.create(parentProvides)
這一個(gè)整體也當(dāng)做一個(gè)表達(dá)式,這一個(gè)整體賦值表達(dá)式的計(jì)算結(jié)果是賦值給了最左側(cè)的providescurrentInstance.provides = Object.create(parentProvides)
這個(gè)表達(dá)式同時(shí)也是一個(gè)賦值表達(dá)式,Object.create(parentProvides)創(chuàng)建了一個(gè)新的引用賦值給了currentInstance這個(gè)引用上的provides屬性
currentInstance.provides
這個(gè)表達(dá)式的語(yǔ)義是:
計(jì)算單值表達(dá)式currentInstance,得到currentInstance的引用
將右側(cè)的名字provides理解為一個(gè)標(biāo)識(shí)符,并作為“.”運(yùn)算的右操作數(shù)
計(jì)算currentInstance.provides
表達(dá)式的結(jié)果(Result)
currentInstance.provides
當(dāng)它作為賦值表達(dá)式的左操作數(shù)時(shí),它是一個(gè)被賦值的引用,而當(dāng)它作為右操作數(shù)時(shí),則計(jì)算它的值。
注意:賦值表達(dá)式左側(cè)的操作數(shù)可以是另一個(gè)表達(dá)式,而在聲明語(yǔ)句中的等號(hào)左邊,絕不可能是一個(gè)表達(dá)式。 例如上面的如果寫(xiě)成了let provides = xxx,那么這個(gè)時(shí)候,provides只是一個(gè)表達(dá)名字的、靜態(tài)語(yǔ)法分析期作為標(biāo)識(shí)符來(lái)理解的字面文本,而不是一個(gè)表達(dá)式。
關(guān)于“Vue3中Provide和Inject的實(shí)現(xiàn)原理是什么”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“Vue3中Provide和Inject的實(shí)現(xiàn)原理是什么”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
文章題目:Vue3中Provide和Inject的實(shí)現(xiàn)原理是什么
地址分享:http://jinyejixie.com/article2/posdoc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、品牌網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站排名、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)