当前位置: 首页 > news >正文

Vue3的provide和inject实现多级传递的原理

先来看个demo,这个是父组件,代码如下:

<template><ChildDemo />
</template><script setup>
import ChildDemo from "./child.vue";
import { ref, provide } from "vue";
// 提供响应式的值
const count = ref(0);
provide("count", count);
</script>

在父组件中使用provide为后代组件注入一个count响应式变量。
再来看看子组件child.vue代码如下:

<template><GrandChild />
</template>
<script setup>
import GrandChild from "./grand-child.vue";
</script>

从上面的代码可以看到在子组件中什么事情都没做,只渲染了孙子组件。
我们再来看看孙子组件grand-child.vue,代码如下:

<script setup>
import { inject } from "vue";// 注入响应式的值
const count = inject("count");
console.log("inject count is:", count);
</script>

从上面的代码可以看到在孙子组件中使用inject函数拿到了父组件中注入的count响应式变量。
provide函数
我们先来debug看看provide函数的代码,给父组件中的provide函数打个断点,如下图:
在这里插入图片描述
刷新页面,此时代码将会停留在断点处。让断点走进provide函数,代码如下:

function provide(key, value) {if (!currentInstance) {if (!!(process.env.NODE_ENV !== "production")) {warn$1(`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] = value;}
}

首先判断currentInstance是否有值,如果没有就说明当前没有vue实例,也就是说当前调用provide函数的地方是不在setup函数中执行的,然后给出警告provide只能在setup中使用。然后走进else逻辑中,首先从当前vue实例中取出存的provides属性对象。并且通过currentInstance.parent.provides拿到父组件vue实例中的provides属性对象。这里为什么需要判断if (parentProvides === provides)呢?因为在创建子组件时会默认使用父组件的provides属性对象作为父组件的provides属性对象。代码如下:

const instance: ComponentInternalInstance = {uid: uid++,vnode,type,parent,provides: parent ? parent.provides : Object.create(appContext.provides),// ...省略
} 

从上面的代码可以看到如果有父组件,那么创建子组件实例的时候就直接使用父组件的provides属性对象。所以这里在provide函数中需要判断if (parentProvides === provides),如果相等说明当前父组件和子组件是共用的同一个provides属性对象。此时如果子组件调用了provide函数,说明子组件需要创建自己的provides属性对象。并且新的属性对象还需要能够访问到父组件中注入的内容,所以这里以父组件的provides属性对象为原型去创建一个新的子组件的,这样在子组件中不仅能够访问到原型链中注入的provides属性对象,也能够访问到自己注入进去的provides属性对象。最后就是执行provides[key] = value将当前注入的内容存到provides属性对象中。
inject函数
我们再来看看inject函数是如何隔了一层子组件从父组件中如何取出数据的,还是一样的套路,给孙子组件中的inject函数打个断点。如下图:
在这里插入图片描述
将断点走进inject函数,代码如下:

export function inject(key: InjectionKey<any> | string,defaultValue?: unknown,treatDefaultAsFactory = false,
) {// fallback to `currentRenderingInstance` so that this can be called in// a functional componentconst instance = currentInstance || currentRenderingInstance// also support looking up from app-level provides w/ `app.runWithContext()`if (instance || currentApp) {const provides = currentApp? currentApp._context.provides: instance? instance.parent == null? instance.vnode.appContext && instance.vnode.appContext.provides: instance.parent.provides: undefinedif (provides && key in provides) {return provides[key]} else if (arguments.length > 1) {return treatDefaultAsFactory && isFunction(defaultValue)? defaultValue.call(instance && 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.`)}
}

首先拿到当前渲染的vue实例赋值给本地变量instance。接着使用if (instance || currentApp)判断当前是否有vue实例,如果没有看看有没有使用app.runWithContext手动注入了上下文,如果注入了那么currentApp就有值。接着就是一串三元表达式,如果使用app.runWithContext手动注入了上下文,那么就优先从注入的上下文中取出provides属性对象。如果没有那么就看当前组件是否满足instance.parent == null,也就是说当前组件是否是根节点。如果是根节点就取app中注入的provides属性对象。如果上面的都不满足就去取父组件中注入的provides属性对象,前面我们讲过了在inject函数阶段,如果子组件内没有使用inject函数,那么就会直接使用父组件的provides属性对象。如果子组件中使用了inject函数,那么就以父组件的provides属性对象为原型去创建一个新的子组件的provides属性对象,从而形成一条原型链。所以这里的孙子节点的provides属性对象中当然就能够拿到父组件中注入的count响应式变量,那么if (provides && key in provides)就满足条件,最后会走到return provides[key]中将父组件中注入的响应式变量count原封不动的返回。还有就是如果我们inject一个没有使用provide存入的key,并且传入了第二个参数defaultValue,此时else if (arguments.length > 1)就满足条件了。在里面会去判断是否传入第三个参数treatDefaultAsFactory,如果这个参数的值为true,说明第二个参数defaultValue可能是一个工厂函数。那么就执行defaultValue.call(instance && instance.proxy)将defaultValue的当中工厂函数的执行结果进行返回。如果第三个参数treatDefaultAsFactory的值不为true,那么就直接将第二个参数defaultValue当做默认值返回。

总结
这篇文章讲了使用provide和inject函数是如何实现数据多级传递的。在创建vue组件实例时,子组件的provides属性对象会直接使用父组件的provides属性对象。如果在子组件中使用了provide函数,那么会以父组件的provides属性对象为原型创建一个新的provides属性对象,并且将provide函数中注入的内容塞到新的provides属性对象中,从而形成了原型链。在孙子组件中,他的parent就是子组件。前面我们讲过了如果没有在组件内使用provide注入东西(很明显这里的子组件确实没有注入任何东西),那么就会直接使用他的父组件的provides属性对象,所以这里的子组件是直接使用的是父组件中的provides属性对象。所以在孙子组件中可以直接使用inject函数拿到父组件中注入的内容。

相关文章:

Vue3的provide和inject实现多级传递的原理

先来看个demo&#xff0c;这个是父组件&#xff0c;代码如下&#xff1a; <template><ChildDemo /> </template><script setup> import ChildDemo from "./child.vue"; import { ref, provide } from "vue"; // 提供响应式的值 c…...

使用html2canvas实现前端截图

一、主要功能 网页截图&#xff1a;html2canvas通过读取DOM结构和元素的CSS样式&#xff0c;在客户端生成图像&#xff0c;不依赖于服务端的渲染。它可以将指定的DOM元素渲染为画布&#xff08;canvas&#xff09;&#xff0c;并生成图像。多种输出格式&#xff1a;生成的图像…...

使用 Python 爬取某网站简历模板(bs4/lxml+协程)

使用 Python 爬取站长素材简历模板 简介 在本教程中&#xff0c;我们将学习如何使用 Python 来爬取站长素材网站上的简历模板。我们将使用requests和BeautifulSoup库来发送 HTTP 请求和解析 HTML 页面。本教程将分为两个部分&#xff1a;第一部分是使用BeautifulSoup的方法&am…...

深度学习模型中音频流式处理

音频流式处理的介绍 在现代深度学习应用中&#xff0c;音频处理是一个重要的领域&#xff0c;尤其是在语音识别、音乐生成和音频分类等任务中。流式处理&#xff08;Streaming Processing&#xff09;是一种有效的处理方式&#xff0c;它允许模型逐帧处理音频数据&#xff0c;…...

C语言(字符数组和字符指针)

字符串实现 在C语言中&#xff0c;表示一个字符串有以下两种形式&#xff1a; 用字符数组存放一个字符串。用字符指针指向一个字符串。 案例 #include <stdio.h>/*** 方式1&#xff1a;使用字符数组实现字符串*/ void str_test1(){// 定义一个伪字符串char str[] &q…...

SkyWalking Helm Chart 4.7.0 安装、配置

https://skywalking.apache.org/events/release-apache-skywalking-kubernetes-helm-chart-4.7.0/https://github.com/apache/skywalking-helm/tree/v4.7.0https://skywalking.apache.org/zh/2020-04-19-skywalking-quick-start/简介 skywalking 是分布式系统的 APM(Applicat…...

微搭低代码AI组件单词消消乐从0到1实践

目录 1 为什么要开发单词消消乐2 需要具备什么功能3 采用什么技术方案实现4 逻辑设计4.1 数据结构设计4.2 游戏的核心逻辑4.3 数据设计 5 代码详解5.1 导入依赖5.2 定义函数组件5.3 数据初始化5.4 状态定义5.5 打乱解释的逻辑5.6 定义选择单词的函数5.7 定义选择解释的函数5.8 …...

23种设计模式之中介者模式

目录 1. 简介2. 代码2.1 Mediator &#xff08;中介者接口&#xff09;2.2 ChatRoom &#xff08;具体中介者类&#xff09;2.3 User &#xff08;同事接口&#xff09;2.4 ChatUser &#xff08;具体同事类&#xff09;2.5 Test &#xff08;测试&#xff09;2.6 运行结果 3. …...

【Golang】Go语言编程思想(六):Channel,第六节,并发编程模式

并发模式 下例重新对 channel 的用法进行回顾&#xff1a; package mainimport ("fmt""math/rand""time" )func msgGen(name string) chan string {c : make(chan string)go func(name string) { // 在这个 goroutine 当中向外发送数据i : 0fo…...

unity打包web,如何减小文件体积,特别是 Build.wasm.gz

unity打包WebGL&#xff0c;使用的是wasw&#xff0c;最终生成的Build.wasm.gz体积很大&#xff0c;有6.5M&#xff0c;有几个方法可以稍微减小这个文件的大小 1. 裁剪引擎代码&#xff1a; 此步可将大小从6.5减小到 6.2&#xff08;此项默认开启&#xff0c;只是改了裁剪等级…...

go引入skywalking

前置条件&#xff1a;安装好jdk11&#xff0c;linux服务器&#xff08;centos7.9&#xff09;&#xff0c;go版本&#xff08;我的是1.18&#xff0c;1.21都可以&#xff09; 1.下载skywalking Downloads | Apache SkyWalking 2.下载agent源码 Downloads | Apache SkyWalkin…...

大华DSS数字监控系统 attachment_downloadAtt.action 任意文件下载漏洞复现

0x01 产品描述: 大华 DSS 数字监控系统是大华开发的一款安防视频监控系统,拥有实时监视、云台操作、录像回放、报警处理、设备管理等功能。0x02 漏洞描述: 大华DSS数字监控系统 attachment_downloadAtt.action接口存在任意文件读取漏洞,未经身份验证攻击者可通过该漏洞读取…...

qt 封装 调用 dll

这个目录下 &#xff0c;第一个收藏的这个 &#xff0c;可以用&#xff0c; 但是有几个地方要注意 第一.需要将dll的头文件添加到qt的文件夹里面 第二&#xff0c;需要在pro文件里面添加动态库路径 第三&#xff0c;如果调用dll失败&#xff0c;那么大概需要将dll文件放在e…...

Python使用Selenium库获取 网页节点元素、名称、内容的方法

我们要用到一些网页源码信息&#xff0c;例如获取一些节点的class内容&#xff0c; 除了使用Beautifulsoup来解析&#xff0c;还可以直接用Selenium库打印节点&#xff08;元素&#xff09;名称&#xff0c;用来获取元素的文本内容或者标签名。 例如获取下面的class的内容&am…...

系统安全——访问控制访问控制

访问控制 概念 什么是访问控制 access control 为用户对系统资源提供最大限度共享的基础上&#xff0c;对用户的访问权进行管理&#xff0c;防止对信息的非授权篡改和滥用 ​ 访问控制作用 保证用户在系统安全策略下正常工作 拒绝非法用户的非授权访问请求 拒绝合法用户越权…...

SQL Server 数据库还原到某个时点(完整恢复模式)

将 SQL Server 数据库还原到某个时点&#xff08;完整恢复模式&#xff09; 适用范围&#xff1a; SQL Server 本主题介绍如何使用 SQL Server Management Studio 或 Transact-SQL 将数据库还原到 SQL Server 中的某个时间点。 本主题仅与使用完整恢复模式或大容量日志恢复模…...

埃隆马斯克X-AI发布Grok-2大模型,快来体验~

引言 近年来&#xff0c;人工智能技术的快速发展推动了大语言模型的广泛应用。无论是日常生活中的智能助手&#xff0c;还是行业中的自动化解决方案&#xff0c;大语言模型都扮演着越来越重要的角色。2024年&#xff0c;X-AI推出了新一代的大模型——Grok-2&#xff0c;这款模…...

Python工厂设计模式:简化对象创建

Python工厂设计模式&#xff1a;简化对象创建 引言什么是工厂模式&#xff1f;简单工厂模式示例定义基类和子类创建工厂类使用工厂创建对象 优点使用场景总结 引言 在编程中&#xff0c;我们经常需要创建不同的对象&#xff0c;但有时创建对象的逻辑可能会变得复杂。工厂设计模…...

【隐私计算篇】隐私集合求交(PSI)原理深入浅出

隐私集合求交技术是多方安全计算领域的一个子问题&#xff0c;通常也被称为安全求交、隐私保护集合交集或者隐私交集技术等&#xff0c;其目的是允许持有各自数据集的双方或者多方&#xff0c;执行两方或者多方集合的交集计算&#xff0c;当PSI执行完成&#xff0c;一方或者两方…...

工作中常用的8种设计模式

前言 设计模式在我们日常的软件开发中无处不在&#xff0c;它们帮助我们编写更易扩展、更具可读性的代码。 今天结合我实际工作场景和源码实例&#xff0c;跟大家一起聊聊工作中最常用的8种设计模式&#xff0c;希望对你会有所帮助。 1. 单例模式 单例模式确保一个类只有一…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版

1.题目描述 2.思路 当前的元素可以重复使用。 &#xff08;1&#xff09;确定回溯算法函数的参数和返回值&#xff08;一般是void类型&#xff09; &#xff08;2&#xff09;因为是用递归实现的&#xff0c;所以我们要确定终止条件 &#xff08;3&#xff09;单层搜索逻辑 二…...

【java】【服务器】线程上下文丢失 是指什么

目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失&#xff1f; 直观示例说明 为什么上下文如此重要&#xff1f; 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程&#xff0c;代码应该如何实现 推荐方案&#xff1a;使用 ManagedE…...

【靶场】XXE-Lab xxe漏洞

前言 学习xxe漏洞,搭了个XXE-Lab的靶场 一、搭建靶场 现在需要登录,不知道用户名密码,先随便试试抓包 二、判断是否存在xxe漏洞 1.首先登录抓包 看到xml数据解析,由此判断和xxe漏洞有关,但还不确定xxe漏洞是否存在。 2.尝试xxe 漏洞 判断是否存在xxe漏洞 A.send to …...