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

目录

前言

需要从父组件向子组件传递数据时,会使用 props。对于层级不深的父子组件可以通过 props 透传数据,但是当父子层级过深时,数据透传将会变得非常麻烦和难以维护。

而依赖注入则是为了解决 prop 逐级透传 的问题而诞生的,父组件 provide 需要共享给子组件的数据,子组件 inject 使用需要的父组件状态数据,而且可以保持响应式。

使用例子

javascript 复制代码
// 父组件
import { provide, ref } from 'vue'
const msg = ref('hello')
provide(/* 注入名 */ 'message', /* 值 */ msg)

//子组件使用
import { inject } from 'vue' 
const message = inject('message')

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

provide

javascript 复制代码
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
  }
}

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

javascript 复制代码
 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),
    ...
}

关系图:

inject

javascript 复制代码
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
    
  }
}

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

总结

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

相关推荐
托尼沙滩裤4 分钟前
【Vue3】实现屏幕共享惊艳亮相
前端·javascript·vue.js
Humbunklung7 分钟前
分布假设学习笔记
笔记·深度学习·学习
啃火龙果的兔子9 分钟前
前端八股文-vue篇
前端·javascript·vue.js
jdyzzy14 分钟前
从0到1做一个“任务管理系统”:Spring Boot + Vue 实战教程(含源码)
vue.js·spring boot·后端
工业互联网专业30 分钟前
基于Python的热门微博数据可视化分析-Flask+Vue
vue.js·python·flask·毕业设计·源码·课程设计·微博数据可视化
嵌入式@秋刀鱼36 分钟前
《 第三章-招式初成》 C++修炼生涯笔记(基础篇)程序流程结构
linux·开发语言·数据结构·c++·笔记·visual studio code
HaiQinyanAN1 小时前
【学习笔记】重载和重写的注意事项
c++·笔记·学习
Thanks_ks1 小时前
探索现代 Web 开发:从 HTML5 到 Vue.js 的全栈之旅
javascript·vue.js·css3·html5·前端开发·web 开发·全栈实战
BillKu1 小时前
Vue3本地存储实现方案
vue.js
梦境虽美,却不长1 小时前
C语言 学习 文件操作(开关,读写,定位,大小)操作 2025年6月8日12:19:24
c语言·开发语言·学习