天天看点

vue3的provide以及inject底层原理你都知道吗

纯属个人笔记,记录下断点学习vue3中provide以及inject,如果对大家有用处,麻烦点赞/ 收藏/ 关注一条龙
demo地址

简单说下大致原理

  • vue2的provide以及inject跟vue3的还是很大不同的。
  • vue2
    • vue2中在组件实例化过程中,this上会维护上下级关系,每个this上都会有parent,而

      inject

      的原理就是一直寻找父类,直到找到或是到顶层
  • vue3
    • vue3中instance会存在属性

      provides

      ,而每个组件在实例过程中都会将自身的

      provides

      属性,赋值给子类instance的

      provides

      属性
    • 所以vue3中寻找的话是直接寻找父类,没有的话才会按照原型链进行寻找

实例代码

const { reactive, provide, inject, render, h } = Vue

      const MyComponent = {
        setup() {
          debugger
          const personInfo = inject('personInfo')
          return () => h('div', { color: 'red' }, `姓名${personInfo.name} -- 年龄${personInfo.age}`)
        }
      }

      const VueComponent = {
        setup() {
          const state = reactive({ name: 'lihh', age: 20 })

          debugger
          provide('personInfo', state)
          return () => h('div', {}, h(MyComponent))
        }
      }

      render(h(VueComponent), document.getElementById('app'))
           
  • 上述实例中存在两个组件,父组件中通过API

    provide

    将reactive属性传递给子组件
  • 子组件通过

    inject

    来接受,并且进行渲染

源码解析

provide实例原理
// provide 入口 provide必须在setup期间进行使用
export function provide<T>(key: InjectionKey<T> | string | number, value: T) {
  if (!currentInstance) {
    if (__DEV__) {
      warn(`provide() can only be used inside setup().`)
    }
  } else {
    // 当前实例的privides 第一次一定是空的
    const parentProvides =
      currentInstance.parent && currentInstance.parent.provides
      // 如果是初次渲染 父类的provides跟自己的一定是保持一致的。因为是从父类复制过来的
    if (parentProvides === provides) {
      // 赋值一个新的对象 如果将来要找到话 如果找不到会按照原型链向上找的
      provides = currentInstance.provides = Object.create(parentProvides)
    }
    // TS doesn't allow symbol as index type
    // 直接赋值
    provides[key as string] = value
  }
}
           
inject实现原理
// inject 入口以及实现原理
export function inject<T>(key: InjectionKey<T> | string): T | undefined
export function inject<T>(
  key: InjectionKey<T> | string,
  defaultValue: T,
  treatDefaultAsFactory?: false
): T
export function inject<T>(
  key: InjectionKey<T> | string,
  defaultValue: T | (() => T),
  treatDefaultAsFactory: true
): T
export function inject(
  key: InjectionKey<any> | string,
  defaultValue?: unknown,
  treatDefaultAsFactory = false
) {
  // fallback to `currentRenderingInstance` so that this can be called in
  // a functional component
  // inject 必须在setup中完成
  const instance = currentInstance || currentRenderingInstance
  if (instance) {
    // #2400
    // to support `app.use` plugins,
    // fallback to appContext's `provides` if the instance is at root
    // 如果实例的父类为null, 获取自身的provides 反之就是获取父类的provides
    const provides =
      instance.parent == null
        ? instance.vnode.appContext && instance.vnode.appContext.provides
        : instance.parent.provides

        // 如果存在的话 直接获取值
    if (provides && (key as string | symbol) in provides) {
      // TS doesn't allow symbol as index type
      return provides[key as string]
    } else if (arguments.length > 1) {
      // 如果是函数的场合
      return treatDefaultAsFactory && isFunction(defaultValue)
        ? defaultValue.call(instance.proxy)
        : defaultValue
    } else if (__DEV__) {
      warn(`injection "${String(key)}" not found.`)
    }
  } else if (__DEV__) {
    warn(`inject() can only be used inside setup() or functional components.`)
  }
}
           

关键点源码断点

  • 将父类的

    provides

    赋值给自己
    vue3的provide以及inject底层原理你都知道吗
    - 通过

    provide

    设置值
    vue3的provide以及inject底层原理你都知道吗
  • 通过

    inject

    获取值
    vue3的provide以及inject底层原理你都知道吗

继续阅读