最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3中Provide?/?Inject的實(shí)現(xiàn)原理分享

 更新時間:2022年02月17日 12:20:10   作者:cobyte  
provide和inject主要為高階插件/組件庫提供用例,這篇文章主要給大家介紹了關(guān)于Vue3中Provide?/?Inject的實(shí)現(xiàn)原理,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

Vue3 的 Provide / Inject 的實(shí)現(xiàn)原理其實(shí)就是巧妙利用了原型和原型鏈來實(shí)現(xiàn)的,所以在了解Vue3 的 Provide / Inject 的實(shí)現(xiàn)原理之前,我們先復(fù)習(xí)一下原型和原型鏈的知識。

原型和原型鏈的知識回顧

  • prototype 與 __proto__

prototype 一般稱為顯式原型,__proto__一般稱為隱式原型。 每一個函數(shù)在創(chuàng)建之后,在默認(rèn)情況下,會擁有一個名為 prototype 的屬性,這個屬性表示函數(shù)的原型對象。

  • 原型鏈

當(dāng)我們訪問一個JS對象屬性的時候,JS先會在這個對象定義的屬性里找,找不到就會沿著這個對象的__proto__這個隱式原型關(guān)聯(lián)起來的鏈條向上一個對象查找,這個鏈條就叫原型鏈。

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出來的實(shí)例對象,fn1.age是這個實(shí)例對象上屬性,fn1.name則從Fn.prototype原型對象而來,因?yàn)閒n1的__proto__隱式原型就是指向Fn這個函數(shù)的原型對象Fn.prototype。原型鏈某種意義上是讓一個引用類型繼承另一個引用類型的屬性和方法。

function Fn() {}
Fn.prototype.name = 'coboy'
let fn1 = new Fn()
fn1.name = 'cobyte'
console.log(fn1.name) // cobyte

當(dāng)訪問fn1這個實(shí)例對象的屬性name的時候,JS先會在fn1這個實(shí)例對象的屬性里查找,剛好fn1定義了一個name屬性,所以就直接返回自身屬性的值cobyte,否則就會繼續(xù)沿著原型鏈向Fn.prototype上去找,那么就會返回coboy。

復(fù)習(xí)完原型和原型鏈的知識之后,我們就開始進(jìn)入Provide/Inject的實(shí)現(xiàn)原理探索。

使用 Provide

在 setup() 中使用 provide 時,我們首先從 vue 顯式導(dǎo)入 provide 方法。這使我們能夠調(diào)用 provide 來定義每個 property。

provide 函數(shù)允許你通過兩個參數(shù)定義 property

  • name (<String> 類型)
  • value
import { provide } from 'vue'
export default {
  setup() {
    provide('name', 'coboy')
  }
}

provide API實(shí)現(xiàn)原理

那么這個provide API實(shí)現(xiàn)原理是什么呢?

provide 函數(shù)可以簡化為

export function provide(key, value) {
    // 獲取當(dāng)前組件實(shí)例
    const currentInstance: any = getCurrentInstance()
    if(currentInstance) {
        // 獲取當(dāng)前組件實(shí)例上provides屬性
        let { provides } = currentInstance
        // 獲取當(dāng)前父級組件的provides屬性
        const parentProvides = currentInstance.parent.provides
        // 如果當(dāng)前的provides和父級的provides相同則說明還沒賦值
        if(provides === parentProvides) {
            // Object.create() es6創(chuàng)建對象的另一種方式,可以理解為繼承一個對象, 添加的屬性是在原型下。
            provides = currentInstance.provides = Object.create(parentProvides)
        }
        provides[key] = value
    }
}

綜上所述provide API就是通過獲取當(dāng)前組件的實(shí)例對象,將傳進(jìn)來的數(shù)據(jù)存儲在當(dāng)前的組件實(shí)例對象上的provides上,并且通過ES6的新API Object.create把父組件的provides屬性設(shè)置到當(dāng)前的組件實(shí)例對象的provides屬性的原型對象上。

組件實(shí)例對象初始化時provides屬性的處理

源碼位置:runtime-core/src/component.ts

我們通過查看instance對象的源碼,可以看到,在instance組件實(shí)例對象上,存在parent和provides兩個屬性。在初始化的時候如果存在父組件則把父組件的provides賦值給當(dāng)前的組件實(shí)例對象的provides,如果沒有就創(chuàng)建一個新的對象,并且把應(yīng)用上下文的provides屬性設(shè)置為新對象的原型對象上的屬性。

使用 Inject

在 setup() 中使用 inject 時,也需要從 vue 顯式導(dǎo)入。導(dǎo)入以后,我們就可以調(diào)用它來定義暴露給我們的組件方式。

inject 函數(shù)有兩個參數(shù):

  • 要 inject 的 property 的 name
  • 默認(rèn)值 (可選)
import { inject } from 'vue'
export default {
  setup() {
    const name = inject('name', 'cobyte')
    return {
      name
    }
  }
}

inject API實(shí)現(xiàn)原理

那么這個inject API實(shí)現(xiàn)原理是什么呢?

inject 函數(shù)可以簡化為

export function inject(
  key,
  defaultValue,
  treatDefaultAsFactory = false
) {
  // 獲取當(dāng)前組件實(shí)例對象
  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個參數(shù)以上
      return treatDefaultAsFactory && isFunction(defaultValue)
        // 如果默認(rèn)內(nèi)容是個函數(shù)的,就執(zhí)行并且通過call方法把組件實(shí)例的代理對象綁定到該函數(shù)的this上
        ? defaultValue.call(instance.proxy) 
        : defaultValue
    }
  }
}

通過inject源碼分析我們可以知道,inject里面先獲取當(dāng)前組件的實(shí)例對象,然后判斷是否根組件,如果是根組件則返回到appContext的provides,否則就返回父組件的provides。

如果當(dāng)前獲取的key在provides上有值,那么就返回該值,如果沒有則判斷是否存在默認(rèn)內(nèi)容,默認(rèn)內(nèi)容如果是個函數(shù),就執(zhí)行并且通過call方法把組件實(shí)例的代理對象綁定到該函數(shù)的this上,否則就直接返回默認(rèn)內(nèi)容。

provide/inject實(shí)現(xiàn)原理總結(jié)

通過上面的分析,可以得知provide/inject實(shí)現(xiàn)原理還是比較簡單的,就是巧妙地利用了原型和原型鏈進(jìn)行數(shù)據(jù)的繼承和獲取。provide API調(diào)用設(shè)置的時候,設(shè)置父級的provides為當(dāng)前provides對象原型對象上的屬性,在inject獲取provides對象中的屬性值時,優(yōu)先獲取provides對象自身的屬性,如果自身查找不到,則沿著原型鏈向上一個對象中去查找。

拓展:Object.create原理

方法說明

  • Object.create()方法創(chuàng)建一個新的對象,并以方法的第一個參數(shù)作為新對象的__proto__屬性的值(以第一個參數(shù)作為新對象的構(gòu)造函數(shù)的原型對象)
  • Object.create()方法還有第二個可選參數(shù),是一個對象,對象的每個屬性都會作為新對象的自身屬性,對象的屬性值以descriptor(Object.getOwnPropertyDescriptor(obj, 'key'))的形式出現(xiàn),且enumerable默認(rèn)為false

源碼模擬

Object.myCreate = function (proto, propertyObject = undefined) {
    if (propertyObject === null) {
        // 這里沒有判斷propertyObject是否是原始包裝對象
        throw 'TypeError'
    } else {
        function Fn () {}
        // 設(shè)置原型對象屬性
        Fn.prototype = proto
        const obj = new Fn()
        if (propertyObject !== undefined) {
            Object.defineProperties(obj, propertyObject)
        }
        if (proto === null) {
            // 創(chuàng)建一個沒有原型對象的對象,Object.create(null)
            obj.__proto__ = null
        }
        return obj
    }
}

定義一個空的構(gòu)造函數(shù),然后指定構(gòu)造函數(shù)的原型對象,通過new運(yùn)算符創(chuàng)建一個空對象,如果發(fā)現(xiàn)傳遞了第二個參數(shù),通過Object.defineProperties為創(chuàng)建的對象設(shè)置key、value,最后返回創(chuàng)建的對象即可。

示例

// 第二個參數(shù)為null時,拋出TypeError
// const throwErr = Object.myCreate({name: 'coboy'}, null)  // Uncaught TypeError
// 構(gòu)建一個以
const obj1 = Object.myCreate({name: 'coboy'})
console.log(obj1)  // {}, obj1的構(gòu)造函數(shù)的原型對象是{name: 'coboy'}
const obj2 = Object.myCreate({name: 'coboy'}, {
    age: {
        value: 18,
        enumerable: true
    }
})
console.log(obj2)  // {age: 18}, obj2的構(gòu)造函數(shù)的原型對象是{name: 'coboy'}

拓展:兩個連續(xù)賦值的表達(dá)式

provides = currentInstance.provides = Object.create(parentProvides) 發(fā)生了什么?

Object.create(parentProvides) 創(chuàng)建了一個新的對象引用,如果只是把 currentInstance.provides 更新為新的對象引用,那么provides的引用還是舊的引用,所以需要同時把provides的引用也更新為新的對象引用。

來自《JavaScript權(quán)威指南》的解析

  • JavaScript總是嚴(yán)格按照從左至右的順序來計算表達(dá)式
  • 一切都是表達(dá)式,一切都是運(yùn)算
provides = currentInstance.provides = Object.create(parentProvides)

上述的provides是一個表達(dá)式,它被嚴(yán)格地稱為“賦值表達(dá)式的左手端(Ihs)操作數(shù)”。 而右側(cè) currentInstance.provides = Object.create(parentProvides) 這一個整體也當(dāng)做一個表達(dá)式,這一個整體賦值表達(dá)式的計算結(jié)果是賦值給了最左側(cè)的provides currentInstance.provides = Object.create(parentProvides) 這個表達(dá)式同時也是一個賦值表達(dá)式,Object.create(parentProvides)創(chuàng)建了一個新的引用賦值給了currentInstance這個引用上的provides屬性

currentInstance.provides這個表達(dá)式的語義是:

  • 計算單值表達(dá)式currentInstance,得到currentInstance的引用
  • 將右側(cè)的名字provides理解為一個標(biāo)識符,并作為“.”運(yùn)算的右操作數(shù)
  • 計算currentInstance.provides表達(dá)式的結(jié)果(Result)

currentInstance.provides當(dāng)它作為賦值表達(dá)式的左操作數(shù)時,它是一個被賦值的引用,而當(dāng)它作為右操作數(shù)時,則計算它的值。

注意:賦值表達(dá)式左側(cè)的操作數(shù)可以是另一個表達(dá)式,而在聲明語句中的等號左邊,絕不可能是一個表達(dá)式。 例如上面的如果寫成了let provides = xxx,那么這個時候,provides只是一個表達(dá)名字的、靜態(tài)語法分析期作為標(biāo)識符來理解的字面文本,而不是一個表達(dá)式。

總結(jié)

到此這篇關(guān)于Vue3中Provide / Inject實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Vue3 Provide / Inject實(shí)現(xiàn)原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue 解決provide和inject響應(yīng)的問題

    vue 解決provide和inject響應(yīng)的問題

    這篇文章主要介紹了vue 解決provide和inject響應(yīng)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • Vue插槽簡介和使用示例詳解

    Vue插槽簡介和使用示例詳解

    插槽就是子組件中的提供給父組件使用的一個占位符,用<slot></slot>?表示,父組件可以在這個占位符中填充任何模板代碼,如?HTML、組件等,填充的內(nèi)容會替換子組件的<slot></slot>標(biāo)簽,這篇文章主要介紹了Vue插槽的理解和使用,需要的朋友可以參考下
    2023-03-03
  • vue樣式疊層z-index不起作用的解決方案

    vue樣式疊層z-index不起作用的解決方案

    這篇文章主要介紹了vue樣式疊層z-index不起作用的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vuex中actions優(yōu)雅處理接口請求的方法

    Vuex中actions優(yōu)雅處理接口請求的方法

    在項(xiàng)目開發(fā)中,如果使用到了 vuex,通常我會將所有的接口請求單獨(dú)用一個文件管理,這篇文章主要介紹了Vuex中actions如何優(yōu)雅處理接口請求,業(yè)務(wù)邏輯寫在 actions 中,本文給大家分享完整流程需要的朋友可以參考下
    2022-11-11
  • vue實(shí)現(xiàn)簽到日歷效果

    vue實(shí)現(xiàn)簽到日歷效果

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)簽到日歷效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • el-elementUI使用el-date-picker選擇年月

    el-elementUI使用el-date-picker選擇年月

    本文主要介紹了el-elementUI使用el-date-picker選擇年月,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-02-02
  • 可能是vue中使用axios最詳細(xì)教程

    可能是vue中使用axios最詳細(xì)教程

    Axios是一個基于Promise用于瀏覽器和nodejs的HTTP客戶端,本質(zhì)上也是對原生XHR的封裝,只不過它是Promise的實(shí)現(xiàn)版本,符合最新的ES規(guī)范,下面這篇文章主要給大家介紹了關(guān)于vue中使用axios最詳細(xì)教程的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • vue3簡易實(shí)現(xiàn)proxy代理實(shí)例詳解

    vue3簡易實(shí)現(xiàn)proxy代理實(shí)例詳解

    這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)學(xué)生成績管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 在vue項(xiàng)目中使用element-ui的Upload上傳組件的示例

    在vue項(xiàng)目中使用element-ui的Upload上傳組件的示例

    本篇文章主要介紹了在vue項(xiàng)目中使用element-ui的Upload上傳組件的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • Vue實(shí)現(xiàn)移動端左右滑動效果的方法

    Vue實(shí)現(xiàn)移動端左右滑動效果的方法

    最近得到一個新需求,需要在Vue項(xiàng)目的移動端頁面上加上左右滑動效果,經(jīng)過一番折騰,最終決定四月vue-touch。下面小編把實(shí)現(xiàn)代碼分享給大家,感興趣的朋友一起看看吧
    2018-11-11

最新評論

芦山县| 聂拉木县| 平邑县| 扎赉特旗| 沭阳县| 来凤县| 尼玛县| 灵台县| 嘉义县| 民县| 繁昌县| 丹棱县| 班玛县| 屏边| 喀什市| 三江| 金乡县| 德清县| 陈巴尔虎旗| 林周县| 孝昌县| 蒙城县| 额济纳旗| 临泽县| 安化县| 阜平县| 富宁县| 延津县| 密云县| 广元市| 建平县| 浦县| 宁武县| 新兴县| 尚义县| 广东省| 海宁市| 西宁市| 东海县| 勐海县| 辽阳市|