Vue3学习记录(六)--- 组合式API之依赖注入和异步组件
一、依赖注入
1、简介
在前面的笔记中,我们学习过父组件向子组件传递数据时,需要借助props
来实现。但如果父组件想要向孙子组件传递数据,那就要连续使用两层props
逐级向下传递,如果要接收数据的是更深层的后代组件,则需要连续使用多层props
进行逐级传递,代码十分繁琐。
依赖注入(provide/inject
)就是来解决这一问题的,该特性可以实现父组件向其后代组件跨层级传递数据,只要父组件向其后代组件提供了依赖(provide
),无论两者中间相隔多少组件层级,后代组件都可以注入(inject
)父组件提供的依赖,从而实现跨组件层级的数据交互。但过多的使用provide/inject
特性,会增加组件之间的耦合性,降低组件的可复用性,因此不可滥用。
2、provide(提供依赖)
局部provide:
父组件要给后代组件传递数据(提供依赖),需要使用procide(name,value)
函数,该函数有两个参数:参数name
表示的是传递数据的名称(依赖名称),可以是一个字符串或者一个Symbol
,后代组件需要通过依赖名称来查找注入数据的值;参数value
表示传递数据的值(依赖值),可以是任意类型的数据,可以是一个数据常量,也可以是一个响应式变量ref
。
如果注入的数据值是一个响应式变量,则后代组件在使用该数据时,依旧具有响应式状态。
一个组件可以多次调用 provide()
,使用不同的依赖名称,注入不同的依赖值。
<script setup>
// 导入需要使用的API
import { ref, provide } from 'vue'// 声明一个响应式变量
let count = ref(0);
// 声明一个普通变量
const text = '一个常量'// 使用provide()注入一个响应式变量
provide('count',count);
// 使用provide()注入一个普通js变量
provide('text',text);
</script>
全局provide:
除了在组件中进行依赖注入,还可以在应用层面进行依赖注入,相当于注入了一个全局的依赖,该依赖可以在应用中的所有组件中注入使用。全局依赖注入需要借助main.js
中createApp()
创建的应用实例:
// main.js中导入API
import { createApp, ref } from 'vue'
// 其他。。。// 创建应用实例
const app = createApp(App)
// 声明响应式变量
let test = ref('哈哈哈')
// provide() 传递常量
app.provide('all', 123)
// provide() 传递响应式变量
app.provide('test', test)// 其他。。。
想要声明全局变量除了使用依赖注入之外,还可以利用app.config.globalProperties
声明全局变量。
3、inject(注入依赖)
上层组件通过provide()
提供依赖之后,后代组件需要通过inject(name[,default,boolean])
函数来注入依赖,将上层组件传递的数据接收到当前组件中来。该函数只有一个必选参数name
,表示要注入依赖的名称,也就是上层组件中provide()
的name
参数:
<script setup>
// 导入需要使用的API
import { inject } from 'vue';// 注入响应式变量依赖
let count = inject('count')
console.log(count);
// 注入普通变量依赖
let text = inject('text')
console.log(text);
// 注入全局普通变量依赖
let all = inject('all')
console.log(all);
// 注入全局响应式依赖
let test = inject('test')
console.log(test);
</script>
控制台输出:
根据上面控制台的输出结果可以看出,如果上层组件提供的依赖是一个响应式变量ref
,则通过inject()
注入的依赖将是该ref
对象,不会被解包为变量值,这种方式保持了ref
在上层组件和后代组件之间的响应式变化。
默认值:
默认情况下,后代组件中inject()
注入的依赖必然是上层组件中已经提供的依赖,否则会抛出一个运行时警告。但如果并不要求注入的依赖被提供过,则可以给依赖设置一个默认值。当inject()
未能从上层组件获取到依赖值时,就会将默认值作为依赖的值。这也就是inject()
函数的第一个可选参数:
// 注入一个未提供的依赖 并设置默认值
let test2 = inject('test2','默认值')
console.log(test2); // 默认值
如果默认值需要调用一个函数或者初始化一个类来设置,为了避免在用不到默认值的情况下造成的性能消耗,可以使用工厂函数来创建默认值,并通过inject()
函数的第二个可选参数,表明第二个参数为一个工厂函数:
// 利用工厂函数设置默认值
let test3 = inject('test3',() => new Object(),true)
console.log(test3);
4、响应式变量的依赖注入
我们在上层组件中可以将响应式变量ref
作为提供的依赖,后代组件将该依赖注入之后,也就获取到该ref
,自然也可以在后代组件中修改该ref
的值,但这样会影响到所有注入该依赖的组件,而且逻辑比较混乱。所以针对响应式数据,建议将所有对响应式数据的更新修改操作都集中保持在提供依赖的组件中。
即使需要在后代组件中更新修改响应式数据的值,也可以可以通过在提供依赖的组件中声明并提供一个更新数据的方法。
提供依赖的上层组件:
<script setup>
// 导入需要使用的API
import { ref, provide } from 'vue'
// 声明一个响应式变量
let count = ref(0);
// 声明响应式依赖的更新方法
const changeCount = (n) => {count.value=n
}// 使用provide()注入响应式变量
provide('count',count);
// 使用provide()注入响应式变量的更新方法
provide('changeCount',changeCount)
</script>
注入依赖的后代组件:
<script setup>
// 导入需要使用的API
import { inject } from 'vue';// 注入响应式变量依赖
let count = inject('count')
// 注入响应式变量依赖的更新方法
let changeCount = inject('changeCount')
// 其他。。。
// 调用更新方法修改响应式变量的值
changeCount(1);
// 其他。。。
</script>
5、readonly()
如果想要确保上层组件中提供的依赖数据不被注入依赖的后代组件所修改,则需要借助readonly()
函数来设置依赖只读。如果设置只读的是响应式变量ref
或着复杂数据类型的JS变量,则在后代组件中尝试修改时,上层组件和后代组件中的变量值都不会被修改;如果设置只读的是简单数据类型的JS变量,则在后代组件中尝试修改时,上层组件的变量值不变,下层组件的变量值会变。
因为传递响应式变量和复杂数据类型的JS变量是将本身的引用传递过去了,上层组件和后代组件中引用的同一个ref
;而简单数据类型的JS变量,则将在后代组件中新建了一份变量,与上层组件中的变量并非同一个。
提供依赖的上层组件:
<script setup>
// 导入需要使用的API
import { ref, provide, readonly } from 'vue'
// 声明一个响应式变量
let count = ref(0);
// 声明一个普通变量
const text = '一个常量'// 使用provide()注入一个响应式变量
provide('count',readonly(count));
// 使用provide()注入一个普通js变量
provide('text',readonly(text));
</script>
注入依赖的后代组件:
<script setup>
// 导入需要使用的API
import { inject } from 'vue';// 注入响应式变量依赖
let count = inject('count');
console.log(count.value); // 0
count.value++;
console.log(count.value); // 0
// 注入普通变量依赖
let text = inject('text')
console.log(text); // 一个常量
text = '2222'
console.log(text); // 222 但上层组件中依旧是text
</script>
6、依赖名称冲突
如果上层组件中在提供依赖时,多个依赖使用的同一个依赖名称,则最终后代组件在注入依赖时获取的数据,将是最后一个使用该依赖名称的数据,因为后面的数据会将前面的数据覆盖。。
在构建大型项目时,为了避免依赖名称的冲突,可以使用Symbol
作为依赖的名称,因为Symbol
数据永远不会重复。将所有Symbol
存储到一个单独的js文件中,并将Symbol
导出,这样方便Symbol
在上层组件和后代组件的中使用。
用来存储Symbol
的js文件:
// 将Symbol数据导出
export const oneKey = Symbol()
提供依赖的上层组件:
<script setup>import { provide } from 'vue'import { oneKey } from './keys.js'// 向后代组件提供依赖provide(oneKey, '要提供的数据');
</script>
注入依赖的后代组件:
<script setup>import { inject } from 'vue'import { oneKey } from './keys.js'// 向当前组件注入依赖const test = inject(oneKey);
</script>
更多Symbol
相关内容请查看:JavaScript 之 Symbol 数据类型。
二、异步组件
1、简介
异步组件是指以异步的方式去加载组件,这些组件不会在页面初始加载时立即加载,而是在需要渲染到DOM上的时候才加载,从而可以提高应用的加载速度和性能。
Vue提供了defineAsyncComponent()
方法用来实现异步组件的功能。
2、基本用法
defineAsyncComponent()
方法的参数是一个返回Promise
的加载函数,当从服务器获取组件成功时调用resolve()
,加载失败时调用reject()
。加载成功之后,该方法的返回值是一个包装过的组件,使用变量接收返回值后,就可以通过变量在组件模板中使用异步加载的组件了。
<script setup>
// 导入要使用的方法
import { defineAsyncComponent } from 'vue'// 调用方法 异步加载组件 并将加载的组件赋值给AsyncComp
const AsyncComp = defineAsyncComponent(() => {return new Promise((resolve, reject) => {// ...从服务器获取组件// 加载成功resolve(/* 获取到的组件 */)// 加载失败reject(/* 加载失败 */)})
})
</script><template><div><!-- 正常使用AsyncComp组件 --><AsyncComp /></div>
</template>
3、ES模块动态导入
ES模块动态导入import
,其实也会返回一个Promise
,所以可以动态导入Vue单文件组件并与defineAsyncComponent()
方法搭配使用,实现组件的异步加载。异步加载获取的是一个包装过的组件,它会将接收到的props
和插槽、事件监听器等内容传递给内部加载的异步组件。
Vite 和 Webpack等构建工具也都支持该语法。
<template><div><div v-if="show"><AsyncChild /></div></div>
</template><script setup>
// 导入需要使用的API
import { ref, defineAsyncComponent } from 'vue'
// 声明响应式变量
let show = ref(false)
// 设置定时器 修改响应式变量的值
setTimeout(() => {show.value = true;
},3000)// 异步加载组件 会在页面渲染 <AsyncChild> 时 调用执行
const AsyncChild = defineAsyncComponent(() => {console.log('加载异步组件');return import('../components/Child3.vue')
})
</script>
与普通组件一样,异步组件也支持通过app.component()
注册为全局组件:
// main.js
app.component('AsyncChild', defineAsyncComponent(() =>import('../components/Child3.vue')
))
4、加载与错误状态
既然是异步加载,那就必然会涉及到加载中和加载失败的状态,defineAsyncComponent()
方法在高级选项中提供了处理这些状态方法,包括加载状态显示、加载错误兜底展示等内容:
const AsyncComp = defineAsyncComponent({// 异步加载组件函数 loader: () => import('../components/Child3.vue'),// 加载中状态 展示的组件loadingComponent: LoadingComponent,// 展示加载组件前的延迟时间,默认为 200ms 避免加载太快导致的闪烁问题delay: 200,// 加载失败状态 展示的组件errorComponent: ErrorComponent,// 可以指定 timeout 时间限制,超时后也会显示这里配置的报错组件,默认值是:Infinity 无限timeout: 3000
})
5、Suspense
异步组件可以搭配内置的 <Suspense>
组件一起使用,但<Suspense>
目前还是一项实验性功能,因此这里就不展开介绍了,感兴趣的小伙伴可以查阅相应文档:Suspense
。
相关文章:

Vue3学习记录(六)--- 组合式API之依赖注入和异步组件
一、依赖注入 1、简介 在前面的笔记中,我们学习过父组件向子组件传递数据时,需要借助props来实现。但如果父组件想要向孙子组件传递数据,那就要连续使用两层props逐级向下传递,如果要接收数据的是更深层的后代组件࿰…...

JZ76 删除链表中重复的结点
/*public class ListNode {int val;ListNode next null;ListNode(int val) {this.val val;} } */import java.util.*; public class Solution {public ListNode deleteDuplication(ListNode pHead) {//初步想想法: 弄一个hashmap 然后进行key存储起来。然后 如果存…...

20.2 nginx
20.2 nginx 1. 学习目标2. 介绍2.1 正向代理2.2 反向代理2.3 动态静态资源分离2.4 nginx优缺点3. 安装3.1 Linux安装****************************************************************************************************************************************************…...
MySQL学习Day26——事务基础知识
一、数据库事务概述: 事务是数据库区别于文件系统的重要特性之一,事务会让数据始终保持一致性,能通过事务机制恢复到某个时间点,可以保证提交到数据库的修改不会因为系统崩溃而丢失 1.查看引擎支持事务的情况:只有InnoDB存储引擎支持事务 SHOW ENGINES; 2.基本概念: 事…...

three.js 射线Ray,三维空间中绘制线框
效果: 代码: <template><div><el-container><el-main><div class"box-card-left"><div id"threejs"></div> <div>{{ res1 }}</div> <div>{{ res2 }}</div><…...
【Demo】游戏小地图
简介 该Demo基于2D关卡随机生成项目进行实现,旨在初步探索游戏小地图的制作。 演示 MiniMapDemo 资源下载 百度网盘(提取码:1314) 如果这篇文章对你有帮助,请给作者点个赞吧!...

代码随想录算法训练营Day39 || leetCode 762.不同路径 || 63. 不同路径 II
62.不同路径 每一位的结果等于上方与左侧结果和 class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m,vector(n,0));for (int i 0; i < m; i) dp[i][0] 1;for (int j 0; j < n; j) dp[0][j] 1;for (int i 1; i < m; …...
Qt中parent()函数的使用
情景(需求)抽象: A类对象是B类对象的成员变量。 B类对象是A类对象的父亲。 A类对象中包含按钮,点击按钮,调用B类的成员函数。 示例: A类: #pragma once#include <QWidget> #include "ui_QtWidgetsCla…...
Python基础学习(5)流程控制
文章目录 一. 程序三大执行流程二. 分支结构1.单分支结构(if)2.双分支结构(if..else)3.多分支结构(if..elif..else) 二,缩进(tab键)三,循环结构1.while循环2.for循环①遍历字典 五.break,continue和pass语句1.break,continue2.pass Python基础学习(1)基本…...

代码随想录刷题笔记 DAY 42 | 最后一块石头的重量 II No.1049 | 目标和 No.494 | 一和零 No.474
文章目录 Day 4301. 最后一块石头的重量 II(No. 1049)<1> 题目<2> 笔记<3> 代码 02. 目标和(No. 494)<1> 题目<2> 笔记<3> 代码 03. 一和零(No. 474)<1> 题目&l…...
793.高精度乘法(acwing)
文章目录 793.高精度乘法题目描述高精度乘法 793.高精度乘法 题目描述 给定两个正整数A和B,请你计算A * B的值。 输入格式 共两行,第一行包含整数A,第二行包含整数B。 输出格式 共一行,包含A * B的值。 数据范围 1≤A的长度≤…...

考研经验|如何从考研失败中走出来?
对我来说,太丢人了 其实我在本科的时候在同学眼中,一直很优秀,每年奖学金必有我的,国家励志奖学金,国家奖学金,这种非常难拿的奖学金,我也拿过,本科期间学校有一个公费去新西兰留学的…...
SpringBoot如何修改pom依赖的默认版本号
1、找到SpringBoot父工程依赖。 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.5.RELEASE</version> </parent>2、ctrl 鼠标左键点击<artifact…...

UDP与TCP:了解这两种网络协议的不同之处
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…...
String、StringBuffer基本用法
一、StringBuffer基本用法 1.append():字符串连接操作 StringBuffer sb new StringBuffer();sb.append("a");sb.append("b");sb.append("c");sb.append("哈哈").append("d");System.out.println(sb);2.insert():在任意位…...

蓝桥杯刷题5--GCD和LCM
目录 1. GCD 1.1 性质 1.2 代码实现 2. LCM 2.1 代码实现 3. 习题 3.1 等差数列 3.2 Hankson的趣味题 3.3 最大比例 3.4 GCD 1. GCD 整数a和b的最大公约数是能同时整除a和b的最大整数,记为gcd(a, b) 1.1 性质 GCD有关的题目一般会考核GCD的性质。 …...

LVS+Keepalived 高可用负载均衡集群
一. 高可用集群的相关知识 1.1 高可用(HA)集群和普通集群的比较 ① 普通集群 普通的群集的部署是通过一台度器控制调配多台节点服务器进行业务请求的处理,但是仅仅是一台调度器,就会存在极大的单点故障风险,当该调度…...

【RK3288 Android6, T8PRO 快捷按键 gpio 配置上拉输入】
文章目录 【RK3288 Android6, T8PRO 快捷按键 gpio 配置上拉输入】需求开发过程尝试找到没有用的上拉gpio尝试修改pwm1的gpio的默认上拉模式 改动 【RK3288 Android6, T8PRO 快捷按键 gpio 配置上拉输入】 需求 T8pro想要模仿T10 的 快捷按键ÿ…...

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:LoadingProgress)
用于显示加载动效的组件。 说明: 该组件从API Version 8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 无 接口 LoadingProgress() 创建加载进展组件。 从API version 9开始,该接口支持在ArkTS卡片中使…...

隐私与创新的交汇点:Partisia Blockchain 重绘技术蓝图
正当我们在这个信息泛滥的时代寻找稳固的信任锚点时,区块链技术应运而生,然而,正如任何科技革命都会遇到的挑战,一个重要的问题摆在了我们面前:如何在不牺牲个人隐私的前提下,享受区块链技术带来的好处&…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...

【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...