文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Vue3中Provide和Inject的实现原理是什么

2023-06-29 04:46

关注

这篇文章主要介绍了Vue3中Provide和Inject的实现原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue3中Provide和Inject的实现原理是什么文章都会有所收获,下面我们一起来看看吧。

Vue3中Provide和Inject的实现原理是什么

原型和原型链的知识回顾

prototype 一般称为显式原型,__proto__一般称为隐式原型。 每一个函数在创建之后,在默认情况下,会拥有一个名为 prototype 的属性,这个属性表示函数的原型对象。

当我们访问一个JS对象属性的时候,JS先会在这个对象定义的属性里找,找不到就会沿着这个对象的__proto__这个隐式原型关联起来的链条向上一个对象查找,这个链条就叫原型链。

function Fn() {}Fn.prototype.name = 'coboy'let fn1 = new Fn()fn1.age = 18console.log(fn1.name) // coboyconsole.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

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属性的处理

Vue3中Provide和Inject的实现原理是什么

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

使用 Inject

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

inject 函数有两个参数:

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.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权威指南》的解析

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

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

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

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

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

关于“Vue3中Provide和Inject的实现原理是什么”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Vue3中Provide和Inject的实现原理是什么”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯