中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue3中Provide/Inject的實現原理是什么

發布時間:2022-02-17 15:59:07 來源:億速云 閱讀:122 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“Vue3中Provide/Inject的實現原理是什么”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Vue3中Provide/Inject的實現原理是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

    原型和原型鏈的知識回顧

    • prototype 與 __proto__

    prototype 一般稱為顯式原型,__proto__一般稱為隱式原型。 每一個函數在創建之后,在默認情況下,會擁有一個名為 prototype 的屬性,這個屬性表示函數的原型對象。

    • 原型鏈

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

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

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

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

    復習完原型和原型鏈的知識之后,我們就開始進入Provide/Inject的實現原理探索。

    使用 Provide

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

    provide 函數允許你通過兩個參數定義 property

    • name (<String> 類型)

    • value

    import { provide } from 'vue'
    export default {
      setup() {
        provide('name', 'coboy')
      }
    }

    provide API實現原理

    那么這個provide API實現原理是什么呢?

    provide 函數可以簡化為

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

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

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

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

    Vue3中Provide/Inject的實現原理是什么

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

    使用 Inject

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

    inject 函數有兩個參數:

    • 要 inject 的 property 的 name

    • 默認值 (可選)

    import { inject } from 'vue'
    export default {
      setup() {
        const name = inject('name', 'cobyte')
        return {
          name
        }
      }
    }

    inject API實現原理

    那么這個inject API實現原理是什么呢?

    inject 函數可以簡化為

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

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

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

    provide/inject實現原理總結

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

    拓展:Object.create原理

    方法說明

    • Object.create()方法創建一個新的對象,并以方法的第一個參數作為新對象的__proto__屬性的值(以第一個參數作為新對象的構造函數的原型對象)

    • Object.create()方法還有第二個可選參數,是一個對象,對象的每個屬性都會作為新對象的自身屬性,對象的屬性值以descriptor(Object.getOwnPropertyDescriptor(obj, 'key'))的形式出現,且enumerable默認為false

    源碼模擬

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

    定義一個空的構造函數,然后指定構造函數的原型對象,通過new運算符創建一個空對象,如果發現傳遞了第二個參數,通過Object.defineProperties為創建的對象設置key、value,最后返回創建的對象即可。

    示例

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

    拓展:兩個連續賦值的表達式

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

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

    來自《JavaScript權威指南》的解析

    • JavaScript總是嚴格按照從左至右的順序來計算表達式

    • 一切都是表達式,一切都是運算

    provides = currentInstance.provides = Object.create(parentProvides)

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

    currentInstance.provides這個表達式的語義是:

    • 計算單值表達式currentInstance,得到currentInstance的引用

    • 將右側的名字provides理解為一個標識符,并作為“.”運算的右操作數

    • 計算currentInstance.provides表達式的結果(Result)

    currentInstance.provides當它作為賦值表達式的左操作數時,它是一個被賦值的引用,而當它作為右操作數時,則計算它的值。

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

    讀到這里,這篇“Vue3中Provide/Inject的實現原理是什么”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    兴和县| 克拉玛依市| 安国市| 东辽县| 上犹县| 志丹县| 灌云县| 滕州市| 惠安县| 东台市| 定襄县| 巴林右旗| 民勤县| 广宁县| 东丰县| 南华县| 东乡族自治县| 梅河口市| 察雅县| 万载县| 本溪| 栾城县| 济阳县| 云安县| 安义县| 大埔区| 周宁县| 汝城县| 乐业县| 沾益县| 衡南县| 桂阳县| 南漳县| 姜堰市| 宁波市| 仪征市| 视频| 全椒县| 凤山市| 方山县| 邳州市|