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 获取数据的时候,则会根据原型链的规则进行查找,找不到的话则会返回用户自定义的默认值

相关推荐
东风西巷2 小时前
Avast Cleanup安卓版(手机清理优化) 修改版
android·学习·智能手机·软件需求
冬夜戏雪3 小时前
记录下C盘清理步骤(有效)
经验分享·笔记
我登哥MVP3 小时前
Apache Tomcat 详解
java·笔记·tomcat
二十雨辰3 小时前
vite性能优化
前端·vue.js
明月与玄武3 小时前
浅谈 富文本编辑器
前端·javascript·vue.js
FuckPatience4 小时前
Vue 与.Net Core WebApi交互时路由初探
前端·javascript·vue.js
泽虞4 小时前
《Qt应用开发》笔记
linux·开发语言·c++·笔记·qt
报错小能手4 小时前
linux学习笔记(21)线程同步——互斥锁
linux·笔记·学习
『往事』&白驹过隙;4 小时前
浅谈内存DDR——DDR4性能优化技术
科技·物联网·学习·性能优化·内存·ddr
aklry5 小时前
elpis之学习总结
前端·vue.js