Heim > Web-Frontend > View.js > Lassen Sie uns über die Implementierungsprinzipien von Provide und Inject in Vue3 sprechen

Lassen Sie uns über die Implementierungsprinzipien von Provide und Inject in Vue3 sprechen

青灯夜游
Freigeben: 2022-02-16 20:09:59
nach vorne
2992 Leute haben es durchsucht

Dieser Artikel vermittelt Ihnen ein umfassendes Verständnis von Vue3 und stellt das Implementierungsprinzip von Provide / Inject im Detail vor. Ich hoffe, dass er für alle hilfreich ist!

Lassen Sie uns über die Implementierungsprinzipien von Provide und Inject in Vue3 sprechen

Das Implementierungsprinzip von Provide/Inject von Vue3 wird tatsächlich durch den geschickten Einsatz von Prototypen und Prototypketten realisiert. Bevor wir das Implementierungsprinzip von Provide/Inject von Vue3 verstehen, überprüfen wir zunächst das Wissen über Prototypen und Prototypketten. [Verwandte Empfehlungen: vue.js Video-Tutorial]

Wissensüberprüfung von Prototypen und Prototypketten

  • Prototyp und __proto____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
Nach dem Login kopieren

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
Nach dem Login kopieren

当访问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 &#39;vue&#39;

export default {
  setup() {
    provide(&#39;name&#39;, &#39;coboy&#39;)
  }
}
Nach dem Login kopieren

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
    }
}
Nach dem Login kopieren

综上所述provide API就是通过获取当前组件的实例对象,将传进来的数据存储在当前的组件实例对象上的provides上,并且通过ES6的新API Object.create把父组件的provides属性设置到当前的组件实例对象的provides属性的原型对象上。

组件实例对象初始化时provides属性的处理

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

Lassen Sie uns über die Implementierungsprinzipien von Provide und Inject in Vue3 sprechen

我们通过查看instance对象的源码,可以看到,在instance组件实例对象上,存在parent和provides两个属性。在初始化的时候如果存在父组件则把父组件的provides赋值给当前的组件实例对象的provides,如果没有就创建一个新的对象,并且把应用上下文的provides属性设置为新对象的原型对象上的属性。

使用 Inject

setup() 中使用 inject 时,也需要从 vue 显式导入。导入以后,我们就可以调用它来定义暴露给我们的组件方式。

inject

    Prototyp wird im Allgemeinen als expliziter Prototyp bezeichnet, __proto__ wird im Allgemeinen als impliziter Prototyp bezeichnet. Nachdem jede Funktion erstellt wurde, verfügt sie standardmäßig über eine Eigenschaft namens „Prototyp“, die das Prototypobjekt der Funktion darstellt.
  • Prototypenkette
  • Wenn wir auf die Eigenschaften eines JS-Objekts zugreifen, sucht JS zunächst in den durch das Objekt definierten Eigenschaften. Wenn es es nicht finden kann, folgt es dem versteckten __proto__ von Dieses Objekt wird nach dem nächsten Objekt durchsucht. Diese Kette wird Prototypenkette genannt.

    import { inject } from &#39;vue&#39;
    
    export default {
      setup() {
        const name = inject(&#39;name&#39;, &#39;cobyte&#39;)
        return {
          name
        }
      }
    }
    Nach dem Login kopieren
    fn1 ist das von der Fn-Funktion new erstellte Instanzobjekt, fn1.age ist das Attribut dieses Instanzobjekts, fn1.name stammt vom Prototypobjekt Fn.prototype, da der implizite Prototyp __proto__ von fn1 darauf zeigt zum Prototypobjekt Fn.prototype der Funktion Fn. Eine Prototypenkette ermöglicht es einem Referenztyp, die Eigenschaften und Methoden eines anderen Referenztyps zu erben.

    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
        }
      }
    }
    Nach dem Login kopieren
  • Beim Zugriff auf den Attributnamen des Instanzobjekts fn1 sucht JS zunächst im Attribut des Instanzobjekts fn1. Es kommt vor, dass fn1 ein Namensattribut definiert, sodass es direkt den Wert-Cobyte seines eigenen Attributs zurückgibt, andernfalls Folgen Sie der Prototypenkette, um Fn.prototype zu finden. Anschließend wird Coboy zurückgegeben.

Nachdem wir das Wissen über Prototypen und Prototypenketten überprüft hatten, begannen wir, die Implementierungsprinzipien von Provide/Inject zu untersuchen.

Verwendung von Provide

Bei Verwendung von provide in setup() beginnen wir zunächst mit vue< /code> Importiert explizit die Methode <code>provide. Dadurch können wir provide aufrufen, um jede Eigenschaft zu definieren.

provide-Funktion ermöglicht es Ihnen, Eigenschaften über zwei Parameter zu definieren

Name (<String> Typ)🎜🎜🎜🎜Wert🎜🎜🎜
Object.myCreate = function (proto, propertyObject = undefined) {
    if (propertyObject === null) {
        // 这里没有判断propertyObject是否是原始包装对象
        throw &#39;TypeError&#39;
    } 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
    }
}
Nach dem Login kopieren
Nach dem Login kopieren
🎜Prinzip der Bereitstellung einer API-Implementierung🎜🎜🎜Was ist also das Prinzip der Bereitstellung einer API-Implementierung? 🎜🎜Provide-Funktion kann vereinfacht werden zu 🎜
// 第二个参数为null时,抛出TypeError
// const throwErr = Object.myCreate({name: &#39;coboy&#39;}, null)  // Uncaught TypeError
// 构建一个以
const obj1 = Object.myCreate({name: &#39;coboy&#39;})
console.log(obj1)  // {}, obj1的构造函数的原型对象是{name: &#39;coboy&#39;}
const obj2 = Object.myCreate({name: &#39;coboy&#39;}, {
    age: {
        value: 18,
        enumerable: true
    }
})
console.log(obj2)  // {age: 18}, obj2的构造函数的原型对象是{name: &#39;coboy&#39;}
Nach dem Login kopieren
Nach dem Login kopieren
🎜Zusammenfassend besteht die Bereitstellungs-API darin, das Instanzobjekt der aktuellen Komponente abzurufen, die eingehenden Daten in den Bereitstellungen für das aktuelle Komponenteninstanzobjekt zu speichern und das neue API-Objekt von ES6 zu übergeben .create setzt das Provides-Attribut der übergeordneten Komponente auf das Prototyp-Objekt des Provides-Attributs des aktuellen Komponenteninstanzobjekts. 🎜

🎜Verarbeitung des Provides-Attributs, wenn das Komponenteninstanzobjekt initialisiert wird🎜🎜🎜Quellcode-Speicherort: runtime-core/src/component.ts🎜🎜Lassen Sie uns über die Implementierungsprinzipien von Provide und Inject in Vue3 sprechen🎜🎜Durch einen Blick auf den Quellcode der Instanz Objekt können wir sehen, dass es in der Instanzkomponente des Instanzobjekts zwei Attribute gibt: „Parent“ und „Provides“. Wenn während der Initialisierung eine übergeordnete Komponente vorhanden ist, weisen Sie die Bereitstellungen der übergeordneten Komponente den Bereitstellungen des aktuellen Komponenteninstanzobjekts zu. Wenn nicht, erstellen Sie ein neues Objekt und legen Sie das Bereitstellungsattribut des Anwendungskontexts auf das Attribut des Prototypobjekts des neuen Objekts fest. 🎜

🎜Verwenden Sie Inject🎜🎜🎜Wenn Sie inject in setup() verwenden, müssen Sie auch bei beginnen vue< /code> Expliziter Import. Nach dem Import können wir es aufrufen, um den für uns bereitgestellten Komponentenmodus zu definieren. 🎜🎜<code>inject Die Funktion hat zwei Parameter: 🎜🎜🎜🎜Der Name der Eigenschaft, die injiziert werden soll🎜🎜🎜🎜Standardwert (🎜optional🎜)🎜🎜🎜
provides = currentInstance.provides = Object.create(parentProvides)
Nach dem Login kopieren
Nach dem Login kopieren
🎜🎜inject API-Implementierungsprinzip🎜 🎜 🎜Was ist also das Implementierungsprinzip dieser Injektions-API? 🎜🎜Inject-Funktion kann vereinfacht werden zu 🎜rrreee🎜Durch die Analyse des Inject-Quellcodes können wir erkennen, dass Inject zuerst das Instanzobjekt der aktuellen Komponente erhält und dann bestimmt, ob es sich um eine Root-Komponente handelt kehrt zu den Anbietern von appContext zurück, andernfalls kehrt es zu den bereitgestellten übergeordneten Komponenten zurück. 🎜🎜Wenn der aktuell erhaltene Schlüssel einen Wert hat, geben Sie den Wert zurück. Wenn nicht, stellen Sie fest, ob der Standardinhalt eine Funktion ist, führen Sie ihn aus und binden Sie das Proxy-Objekt der Komponenteninstanz an die Funktion über die Aufrufmethode dieser Funktion, andernfalls wird der Standardinhalt direkt zurückgegeben. 🎜

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 &#39;TypeError&#39;
    } 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
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

定义一个空的构造函数,然后指定构造函数的原型对象,通过new运算符创建一个空对象,如果发现传递了第二个参数,通过Object.defineProperties为创建的对象设置key、value,最后返回创建的对象即可。

示例

// 第二个参数为null时,抛出TypeError
// const throwErr = Object.myCreate({name: &#39;coboy&#39;}, null)  // Uncaught TypeError
// 构建一个以
const obj1 = Object.myCreate({name: &#39;coboy&#39;})
console.log(obj1)  // {}, obj1的构造函数的原型对象是{name: &#39;coboy&#39;}
const obj2 = Object.myCreate({name: &#39;coboy&#39;}, {
    age: {
        value: 18,
        enumerable: true
    }
})
console.log(obj2)  // {age: 18}, obj2的构造函数的原型对象是{name: &#39;coboy&#39;}
Nach dem Login kopieren
Nach dem Login kopieren

拓展:两个连续赋值的表达式

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

Object.create(parentProvides) 创建了一个新的对象引用,如果只是把 currentInstance.provides 更新为新的对象引用,那么provides的引用还是旧的引用,所以需要同时把provides的引用也更新为新的对象引用。

来自《JavaScript权威指南》的解析

  • JavaScript总是严格按照从左至右的顺序来计算表达式
  • 一切都是表达式,一切都是运算
provides = currentInstance.provides = Object.create(parentProvides)
Nach dem Login kopieren
Nach dem Login kopieren

上述的provides是一个表达式,它被严格地称为“赋值表达式的左手端(Ihs)操作数”。 而右侧 currentInstance.provides = Object.create(parentProvides) 这一个整体也当做一个表达式,这一个整体赋值表达式的计算结果是赋值给了最左侧的providescurrentInstance.provides = Object.create(parentProvides) 这个表达式同时也是一个赋值表达式,Object.create(parentProvides)创建了一个新的引用赋值给了currentInstance这个引用上的provides属性

currentInstance.provides这个表达式的语义是:

  • 计算单值表达式currentInstance,得到currentInstance的引用
  • 将右侧的名字provides理解为一个标识符,并作为“.”运算的右操作数
  • 计算currentInstance.provides表达式的结果(Result)

currentInstance.provides当它作为赋值表达式的左操作数时,它是一个被赋值的引用,而当它作为右操作数时,则计算它的值。

注意:赋值表达式左侧的操作数可以是另一个表达式,而在声明语句中的等号左边,绝不可能是一个表达式。 例如上面的如果写成了let provides = xxx,那么这个时候,provides只是一个表达名字的、静态语法分析期作为标识符来理解的字面文本,而不是一个表达式

(学习视频分享:web前端教程

Das obige ist der detaillierte Inhalt vonLassen Sie uns über die Implementierungsprinzipien von Provide und Inject in Vue3 sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage