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 重绘技术蓝图
正当我们在这个信息泛滥的时代寻找稳固的信任锚点时,区块链技术应运而生,然而,正如任何科技革命都会遇到的挑战,一个重要的问题摆在了我们面前:如何在不牺牲个人隐私的前提下,享受区块链技术带来的好处&…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
