稀土掘金 稀土掘金

Vue的provide和inject源码深度解读

provide和inject是什么?

inject()

注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。
第一个参数是注入的 key。Vue 会遍历父组件链,通过匹配 key 来确定所提供的值。如果父组件链上多个组件对同一个 key 提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过 key 匹配到值,inject() 将返回 undefined,除非提供了一个默认值。

provide()

提供一个值,可以被后代组件注入。

总得来说provideinject 主要解决的是跨级组件间的通信问题

使用示例:

import { provide, inject, h } from 'vue';

const ThemeSymbol = Symbol();

const Provider = {
  setup() {
    provide(ThemeSymbol, 'dark');
  },
  render() {
    return this.$slots.default();
  }
}

const Consumer = {
  setup() {
    const theme = inject(ThemeSymbol);
    return { theme };
  },
  render() {
    return h('div', `Theme: ${this.theme}`);
  }
}

const App = {
  components: {
    Provider,
    Consumer
  },
  template: `
    <Provider>
      <Consumer></Consumer>
    </Provider>
  `
}

createApp(App).mount('#app');

源码实现

github1s链接: github1s.com/vuejs/core/…

先看provide源码:

export function provide<T, K = InjectionKey<T> | string | number>(
  key: K,
  value: K extends InjectionKey<infer V> ? V : T,
) {
  if (!currentInstance) {
    if (__DEV__) {
      warn(`provide() can only be used inside setup().`)
    }
  } else {
    let provides = currentInstance.provides
    const parentProvides =
      currentInstance.parent && currentInstance.parent.provides
    if (parentProvides === provides) {
      provides = currentInstance.provides = Object.create(parentProvides)
    }
    provides[key as string] = value
  }
}

从当前组件实例找到provides。找当前组件实例父的provides
如果他们是相等的。那么创建一个原型指向父provides对象作为组件实例的provides
(创建一个新对象是为了维护组件自己的provides
(利用原型链的原理,查找属性的时候如果provides对象没有属性。会沿着原型链找。那么就会找到父组件的provides。不断向上查找。)

注意:
每个组件实例都会有一个 provides 属性,这个属性在组件实例创建时就会被初始化

在初始化过程中,如果一个组件没有声明 provide,那么它的 provides 对象会被设置为其父组件的 provides 对象
这个设计使得 inject 可以跨越任何层级的组件来获取值,而无需担心中间组件是否有 provide

下面这行源码才能解释,为什么可以跨组件传递和获取值? 因为组件初始化就根据组件链构建了provides链

创建组件(createComponentInstance)的时候provides属性已经默认赋值了父组件的providesimage.png

再看inject源码:

export function inject(
  key: InjectionKey<any> | string,
  defaultValue?: unknown,
  treatDefaultAsFactory = false,
) {
  const instance = currentInstance || currentRenderingInstance
  if (instance || currentApp) {
    const provides = instance
      ? instance.parent == null
        ? instance.vnode.appContext && instance.vnode.appContext.provides
        : instance.parent.provides
      : currentApp!._context.provides

    if (provides && (key as string | symbol) in provides) {
      return provides[key as string]
    } else if (arguments.length > 1) {
      return treatDefaultAsFactory && isFunction(defaultValue)
        ? defaultValue.call(instance && instance.proxy)
        : defaultValue
    } 
  } 
}

获取当前组件实例的父组件的provides。并且provides[key]。这时候会沿着provide时候构建的原型链查找并返回。

总结:

明白了组件的provides本质上是利用js的原型链实现这个原理后。
下次面试官问:
如果A -> B -> C 3个组件组件是一个嵌套结构。
那么A和B都provide了一个 foo。
C组件inject('fod')的时候。获取的是A还是B的foo?
就知道 inject本质上是在根据(组件初始化时候)构建的provides链上查找。找到最近一个满足条件(存在查找的key)的上级的provide的属性。

玻璃钢生产厂家保定玻璃钢浮雕雕塑江苏常用玻璃钢雕塑摆件晋城铜校园玻璃钢雕塑厂家鲨鱼玻璃钢门雕塑玻璃钢农民雕塑沈阳玻璃钢雕塑公司招聘玻璃钢古代人物雕塑厂家电话玻璃钢仿真奶茶杯雕塑安吉玻璃钢造型雕塑通化景区玻璃钢雕塑生产厂家浙江户内玻璃钢雕塑优势安徽玻璃钢雕塑供应仿真透明玻璃钢雕塑制作厦门玻璃钢雕塑产品滨州市玻璃钢雕塑定制玻璃钢卡通雕塑哪里有滁州人物玻璃钢雕塑价格江西仿铜玻璃钢雕塑批发春节商场美陈圣诞树嘉定区玻璃钢雕塑价格北京特色商场美陈厂家直销玻璃钢雕塑为什么这么火玻璃钢雕塑图福建特色商场美陈哪家好汕尾动物雕塑玻璃钢图片金华动物玻璃钢雕塑订做价格武威人物玻璃钢雕塑南京节日商场美陈佛山玻璃钢蛇雕塑广东玻璃钢马雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化