• vue3学习源码笔记(小白入门系列)------provide和 inject 跨层级数据传递原理


    前言

    需要从父组件向子组件传递数据时,会使用 props。对于层级不深的父子组件可以通过 props 透传数据,但是当父子层级过深时,数据透传将会变得非常麻烦和难以维护。
    而依赖注入则是为了解决 prop 逐级透传 的问题而诞生的,父组件 provide 需要共享给子组件的数据,子组件 inject 使用需要的父组件状态数据,而且可以保持响应式。

    使用例子

    // 父组件
    import { provide, ref } from 'vue'
    const msg = ref('hello')
    provide(/* 注入名 */ 'message', /* 值 */ msg)
    
    //子组件使用
    import { inject } from 'vue' 
    const message = inject('message')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当传入的数据 是响应式数据时, 源数据被修改后 就会派发更新。 所有使用的该数据 (被依赖收集的)组件都会触发更新

    provide

    
    export function provide<T, K = InjectionKey<T> | string | number>(
      key: K,
      value: K extends InjectionKey<infer V> ? V : T
    ) {
      // 必须在setup 方法体 使用
      if (!currentInstance) {
        if (__DEV__) {
          warn(`provide() can only be used inside setup().`)
        }
      } else {
        // 获取当前组件实例上的 provides 对象
        let provides = currentInstance.provides
        // 获取父组件实例上的 provides 对象
        const parentProvides =
          currentInstance.parent && currentInstance.parent.provides
        // 一般只有 根组件创建时 两者不等 不需要将子组件的provides 原型指向 父组件的provides
        if (parentProvides === provides) {
         
          provides = currentInstance.provides = Object.create(parentProvides)
        }
        // TS doesn't allow symbol as index type
        provides[key as string] = value
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    看下 instance 初始化时 provides是怎么创建的

     const instance: ComponentInternalInstance = {
        uid: uid++,
        vnode,
        type,
        parent,
        appContext,
        root: null!, // to be immediately set
        next: null,
        subTree: null!, // will be set synchronously right after creation
        effect: null!,
        update: null!, // will be set synchronously right after creation
        scope: new EffectScope(true /* detached */),
        render: null,
        proxy: null,
        exposed: null,
        exposeProxy: null,
        withProxy: null,
        // 初始化 provides (根组件的parent为null 原型链就会指向 app实例上的 provides)
        provides: parent ? parent.provides : Object.create(appContext.provides),
        ...
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    关系图:

    在这里插入图片描述

    inject

    export function inject(key, defaultValue, treatDefaultAsFactory = false) {
      // 获取当前组件实例
      const instance = currentInstance || currentRenderingInstance
      if (instance) {
        // 获取父组件上的 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) {
          // 返回默认值
          return treatDefaultAsFactory && isFunction(defaultValue)
          // 如果默认内容是个函数的,就执行并且通过call方法把组件实例的代理对象绑定到该函数的this上
            ? defaultValue.call(instance.proxy)
            : defaultValue
        
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    核心也就是从当前组件实例的父组件上取 provides 对象,然后再查找父组件 provides 上有没有对应的属性。因为父组件的 provides 是通过原型链的方式和父组件的父组件进行了关联,如果父组件上没有,那么会通过原型链的方式再向上取,这也实现了不管组件层级多深,总是可以找到对应的 provide 的提供方数据

    总结

    在执行 provide 的时候,会将父组件的的 provides 关联成当前组件实例 provides 对象原型上的属性,当在 inject 获取数据的时候,则会根据原型链的规则进行查找,找不到的话则会返回用户自定义的默认值

  • 相关阅读:
    嵌入式软件架构设计-建立抽象层
    【自动驾驶】使用同心区域模型改进地面点云快速分割算法
    mysql 大数据量分批添加索引
    RuoYi-Vue-Plus (角色部门-数据权限 @DataPermission使用、自定义数据权限、数据权限拦截 、处理器解读)
    常用消息中间件有哪些
    【WAX链游】发布一个免费开源的Alien Worlds【外星世界】合约脚本TLM
    FPGA project : TFT_LCD
    Linux基础指令笔记大全
    C语言写打包DLL或者so给第三方调用
    JAVA面试技巧之项目介绍
  • 原文地址:https://blog.csdn.net/weixin_45485922/article/details/133789455