Vue3学习记录(六)--- 组合式API之依赖注入和异步组件

news2024/9/29 23:25:31

一、依赖注入

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.jscreateApp()创建的应用实例:

// 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1506240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

同一交换机下不同网段的终端通信

文章目录 一个有趣的实验 大家都知道不同网段的IP地址要想通信需要通过网关进行路由转发&#xff0c;而一般通过路由器来做默认网关。 一个有趣的实验 一台二层交换机下&#xff0c;连接两个不同网段的PC&#xff0c;实现彼此之间的通信。 一台S3700交换机&#xff0c;两台PC。…

【金三银四】Spring面试题

目录 1、什么是Spring2、说一下Spring的IOC3、Spring的AOP4、连接点&#xff1f;切入点&#xff1f;5、Spring AOP 是通过什么实现的6、Spring Bean的生命周期是怎么样的&#xff1f;7、Spring Bean的初始化过程是怎么样8、Spring的事务传播机制有哪些&#xff1f;9、Autowired…

Qt 中Json文件的操作

Json文件的读取 QFile file("data.json"); //准备好的文件file.open(QIODevice::ReadOnly|QIODevice::Text);QByteArray arr file.readAll();QJsonDocument jsonDoc QJsonDocument::fromJson(arr);QJsonObject jsonObj jsonDoc.object();qint32 id jsonObj["…

网络工程师笔记10 ( RIP / OSPF协议 )

RIP 学习路由信息的时候需要配认证 RIP规定超过15跳认定网络不可达 链路状态路由协议-OSPF 1. 产生lsa 2. 生成LSDB数据库 3. 进行spf算法&#xff0c;生成最有最短路径 4. 得出路由表

Python错题集-8:AttributeError(找不到对应的对象的属性)

1问题描述 AttributeError: AxesSubplot object has no attribute arc 2代码详情 import matplotlib.pyplot as plt# 创建一个新的图形和坐标轴 fig, ax plt.subplots()# 定义弧线的参数 center (0.5, 0.5) # 圆心坐标 (x, y) width 1.0 # 半径 height 0.5 # 半径 ang…

学习笔记。。。

1.字符串的拼接 1.sprintf() 往字符串的前面或中间、后面拼接一个字符串。 2.strncpy()用来复制字符串的前n个字符 //dest为目标数组&#xff0c;src为源数组&#xff0c;n为要复制的字符个数 2.char* My_strncpy(char* dest, const char* src, int n) 3.char *strcat(ch…

【Axure高保真原型】可视化动点素材

今天和粉丝们免费分享可视化动点素材的原型模板&#xff0c;该模板使用简单&#xff0c;复制粘贴&#xff0c;预览时即可实现动点效果&#xff0c;本案例提供红黄蓝绿4中颜色的动点&#xff0c;如果需要其他颜色&#xff0c;可以自行编辑svg里面的代码 【原型效果】 【模板下载…

Leetcode 59.螺旋矩阵Ⅱ

1.题目 2.思路 &#xff08;借用代码随想录的图&#xff09; 1.我们将转一圈看作一个循环&#xff08;1->2->3->4->5->6->7->8 这是一个循环&#xff09; 2.在这个循环里&#xff0c;我们要画四条边&#xff08;上右下左&#xff09; 填充上行从左到右 填…

[天天向上] 学习方法论-事半功倍的问题解决方法

目录 一、尝试独立解决问题1. 关于独立2. 像密室逃脱一样 二、提问的艺术1. 合适的自我介绍1.1 群名片2.2 研究方向/业务内容 2. 详细的问题描述2.1 问题描述要点2.2 描述格式2.3 问题内容描述&#xff0c;尤其是当前进展和问题 3. 如何让更多的人为你解答4. 如何结束提问更优雅…

ThreadLocal使用,配合拦截器HandlerInterceptor使用

ThreadLocal使用&#xff0c;配合拦截器HandlerInterceptor使用 ThreadLocal的使用场景通常涉及多线程环境下需要为每个线程保留独立状态的情况。它提供了一种简单的方式来管理线程本地变量&#xff0c;使得每个线程都可以独立地访问和修改自己的变量副本&#xff0c;而不会影…

《C语言文件操作》

目录 1. 文本数据和二进制 1.1 文本数据 1.2 二进制数据 1.3文本文件和二进制文件 2. 文件的打开和关闭 2.1 文件指针 2.2 打开文件 2.3 关闭文件 2.4 注意事项&#xff1a; 3. 文本文件的读写 3.1 向文件中写入数据 3.2 从文件中读取数据 3.3 注意事项 4. 二进制文件的读写…

如何对于单元格数据进行清洗处理

如何对于单元格数据进行清洗处理 陪伴意味着有人愿意把最美好的东西给你&#xff0c; 那就是时间。 当然陪伴也是一个很平常的事情&#xff0c; 日复一日&#xff0c;年复一年。 到最后陪伴就成了一种习惯。 约定好的相逢&#xff0c;伴你天荒地老&#xff01; 陪伴是最长情的告…

Git分支管理(Git分支的原理、创建、切换、合并、删除分支)

系列文章目录 文章一&#xff1a;Git基本操作 文章目录 系列文章目录前言一、Git分支是什么二、Git分支的原理三、创建分支四、切换分支五、合并分支六、删除分支 前言 在上一篇文章中&#xff0c;我们学习了如何使用Git的一些基本操作&#xff0c;例如安装Git、创建本地仓库…

Unity使用Addressable热更新

先看热更新的gif: Addressable是Unity推出的打ab包方案。不需要手动写AB打包脚手架了&#xff0c;不需要关心依赖&#xff0c;这也简化了ab热更新的流程。Addressable打包需要先将资源放入group中&#xff0c;按group来打包&#xff0c;每个group对应一个ScriptableObject的配置…

Unity 关节:铰链、弹簧、固定、物理材质:摩檫力、 特效:拖尾、

组件-物理-关节&#xff1a;铰链&#xff08;类似门轴&#xff09; 自动动作、多少力可以将其断开、 弹簧可以连接另一个刚体&#xff08;拖动即可&#xff09; 固定一般是等待一个断裂力&#xff0c;造成四分五裂的效果。 物理材质 设置摩檫力&#xff0c;则可以创造冰面的…

【算法面试题】-04

执行时长 def min_execution_time(n, size, tasks):a 0ans sizei 0while i < size:tmp tasks[i]a tmpif a < n:a 0else:a - ni 1ans a // nif a % n ! 0:ans 1return ans# 读取输入 n int(input()) size int(input()) tasks list(map(int, input().split()))…

macOS14.4安装FFmpeg及编译FFmpeg源码

下载二进制及源码包 二进制 使用brew安装ffmpeg : brew install ffmpeg 成功更新到ffmpeg6.1 下载FFmpeg源码

SMART PLC自适应低通滤波器(收放卷线速度滤波)

一阶低通滤波器更多内容请参考信号处理专栏相关文章,常用链接如下: 1、SMART PLC 低通滤波器和模拟量采集应用 https://rxxw-control.blog.csdn.net/article/details/136595982https://rxxw-control.blog.csdn.net/article/details/1365959822、SMART PLC双线性变换和后向差…

实拆一个Philips剃须刀

拆卸难度很大&#xff0c;怪不得防水 FR&#xff1a;徐海涛(hunkXu)

【Prometheus】DataModel

数据模型 DataModel 指标 Metric metric 包含 metric name 和 metric label 格式&#xff1a; <metric name>{<label name><label value>, ...}例如&#xff1a;服务器 HTTP 接口 /messages 的总请求数 api_http_requests_total{method"POST",…