Vue3中的组合式API的详细教程和介绍
文章目录
- 前言
- 介绍
- 组合式 API 基础
- setup 组件选项
- 带 ref 的响应式变量
- 生命周期钩子注册内部 setup
- watch 响应式更改
- 独立的 computed 属性
- 后言
前言
hello world欢迎来到前端的新世界
😜当前文章系列专栏:vue.js
🐱👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,感谢大家指出)🌹
💖感谢大家支持!您的观看就是作者创作的动力
介绍
通过创建 Vue 组件,我们可以将接口的可重复部分及其功能提取到可重用的代码段中。仅此一项就可以使我们的应用程序在可维护性和灵活性方面走得更远。然而,我们的经验已经证明,光靠这一点可能是不够的,尤其是当你的应用程序变得非常大的时候——想想几百个组件。在处理如此大的应用程序时,共享和重用代码变得尤为重要。
假设在我们的应用程序中,我们有一个视图来显示某个用户的仓库列表。除此之外,我们还希望应用搜索和筛选功能。处理此视图的组件可能如下所示:
// src/components/UserRepositories.vueexport default {components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },props: {user: { type: String }},data () {return {repositories: [], // 1filters: { ... }, // 3searchQuery: '' // 2}},computed: {filteredRepositories () { ... }, // 3repositoriesMatchingSearchQuery () { ... }, // 2},watch: {user: 'getUserRepositories' // 1},methods: {getUserRepositories () {// 使用 `this.user` 获取用户仓库}, // 1updateFilters () { ... }, // 3},mounted () {this.getUserRepositories() // 1}
}
该组件有以下几个职责:
- 从假定的外部 API 获取该用户名的仓库,并在用户更改时刷新它
- 使用 searchQuery 字符串搜索存储库
- 使用 filters 对象筛选仓库
用组件的选项 (data
、computed
、methods
、watch
) 组织逻辑在大多数情况下都有效。然而,当我们的组件变得更大时,逻辑关注点的列表也会增长。这可能会导致组件难以阅读和理解,尤其是对于那些一开始就没有编写这些组件的人来说。
一个大型组件的示例,其中逻辑关注点是按颜色分组。
这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。
如果我们能够将与同一个逻辑关注点相关的代码配置在一起会更好。而这正是组合式 API 使我们能够做到的。
组合式 API 基础
既然我们知道了为什么,我们就可以知道怎么做。为了开始使用组合式 API
,我们首先需要一个可以实际使用它的地方。在 Vue3
组件中,我们将此位置称为 setup
。
setup 组件选项
新的 setup 组件选项在创建组件之前执行,一旦 props
被解析,并充当合成 API 的入口点。
WARNING
由于在执行setup
时尚未创建组件实例,因此在 setup 选项中没有 this。这意味着,除了 props
之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
setup
选项应该是一个接受 props
和context
的函数,我们将在稍后讨论。此外,我们从 setup
返回的所有内容都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
让我们添加 setup
到我们的组件中:
// src/components/UserRepositories.vueexport default {components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },props: {user: { type: String }},setup(props) {console.log(props) // { user: '' }return {} // 这里返回的任何内容都可以用于组件的其余部分}// 组件的“其余部分”
}
现在让我们从提取第一个逻辑关注点开始 (在原始代码段中标记为“1”)。
从假定的外部 API 获取该用户名的仓库,并在用户更改时刷新它
我们将从最明显的部分开始:
- 仓库列表
- 更新仓库列表的函数
- 返回列表和函数,以便其他组件选项可以访问它们
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'// 在我们的组件内
setup (props) {let repositories = []const getUserRepositories = async () => {repositories = await fetchUserRepositories(props.user)}return {repositories,getUserRepositories // 返回的函数与方法的行为相同}
}
这是我们的出发点,但它还不能工作,因为我们的 repositories
变量是非响应式的。这意味着从用户的角度来看,仓库列表将保持为空。我们来解决这个问题!
带 ref 的响应式变量
在 Vue 3.0 中,我们可以通过一个新的 ref 函数使任何响应式变量在任何地方起作用,如下所示:
import { ref } from 'vue'const counter = ref(0)
ref 接受参数并返回它包装在具有 value property 的对象中,然后可以使用该 property 访问或更改响应式变量的值:
import { ref } from 'vue'const counter = ref(0)console.log(counter) // { value: 0 }
console.log(counter.value) // 0counter.value++
console.log(counter.value) // 1
在对象中包装值似乎不必要,但在 JavaScript
中保持不同数据类型的行为统一是必需的。这是因为在 JavaScript 中,Number 或 String
等基本类型是通过值传递的,而不是通过引用传递的:
在任何值周围都有一个包装器对象,这样我们就可以在整个应用程序中安全地传递它,而不必担心在某个地方失去它的响应性。
提示
换句话说,ref 对我们的值创建了一个响应式引用。使用引用的概念将在整个组合式 API 中经常使用。
回到我们的例子,让我们创建一个响应式的 repositories 变量:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref } from 'vue'// in our component
setup (props) {const repositories = ref([])const getUserRepositories = async () => {repositories.value = await fetchUserRepositories(props.user)}return {repositories,getUserRepositories}
}
完成!现在,每当我们调用 getUserRepositories 时,repositories 都将发生变化,视图将更新以反映更改。我们的组件现在应该如下所示:
// src/components/UserRepositories.vue
import { fetchUserRepositories } from '@/api/repositories'
import { ref } from 'vue'export default {components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },props: {user: { type: String }},setup (props) {const repositories = ref([])const getUserRepositories = async () => {repositories.value = await fetchUserRepositories(props.user)}return {repositories,getUserRepositories}},data () {return {filters: { ... }, // 3searchQuery: '' // 2}},computed: {filteredRepositories () { ... }, // 3repositoriesMatchingSearchQuery () { ... }, // 2},watch: {user: 'getUserRepositories' // 1},methods: {updateFilters () { ... }, // 3},mounted () {this.getUserRepositories() // 1}
}
我们已经将第一个逻辑关注点中的几个部分移到了 setup 方法中,它们彼此非常接近。剩下的就是在 mounted
钩子中调用 getUserRepositories
,并设置一个监听器,以便在 user prop
发生变化时执行此操作。
我们将从生命周期钩子开始。
生命周期钩子注册内部 setup
为了使组合式 API 的特性与选项式 API 相比更加完整,我们还需要一种在 setup 中注册生命周期钩子的方法。这要归功于从 Vue
导出的几个新函数。组合式API
上的生命周期钩子与选项式 API
的名称相同,但前缀为 on:即 mounted
看起来像 onMounted
。
这些函数接受在组件调用钩子时将执行的回调。
让我们将其添加到 setup
函数中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted } from 'vue'// in our component
setup (props) {const repositories = ref([])const getUserRepositories = async () => {repositories.value = await fetchUserRepositories(props.user)}onMounted(getUserRepositories) // on `mounted` call `getUserRepositories`return {repositories,getUserRepositories}
}
现在我们需要对 user prop 所做的更改做出反应。为此,我们将使用独立的 watch 函数。
watch 响应式更改
就像我们如何使用 watch 选项在组件内的 user property 上设置侦听器一样,我们也可以使用从 Vue 导入的 watch 函数执行相同的操作。它接受 3 个参数:
- 一个响应式引用或我们想要侦听的 getter 函数
- 一个回调
- 可选的配置选项
下面让我们快速了解一下它是如何工作的
import { ref, watch } from 'vue'const counter = ref(0)
watch(counter, (newValue, oldValue) => {console.log('The new counter value is: ' + counter.value)
})
例如,每当 counter
被修改时 counter.value=5
,watch
将触发并执行回调 (第二个参数),在本例中,它将把 'The new counter value is:5'
记录到我们的控制台中。
以下是等效的选项式 API:
export default {data() {return {counter: 0}},watch: {counter(newValue, oldValue) {console.log('The new counter value is: ' + this.counter)}}
}
现在我们将其应用到我们的示例中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'// 在我们组件中
setup (props) {// 使用 `toRefs` 创建对prop的 `user` property 的响应式引用const { user } = toRefs(props)const repositories = ref([])const getUserRepositories = async () => {// 更新 `prop.user` 到 `user.value` 访问引用值repositories.value = await fetchUserRepositories(user.value)}onMounted(getUserRepositories)// 在用户 prop 的响应式引用上设置一个侦听器watch(user, getUserRepositories)return {repositories,getUserRepositories}
}
你可能已经注意到在我们的 setup 的顶部使用了 toRefs。这是为了确保我们的侦听器能够对 user prop 所做的更改做出反应。
有了这些变化,我们就把第一个逻辑关注点移到了一个地方。我们现在可以对第二个关注点执行相同的操作——基于 searchQuery 进行过滤,这次是使用计算属性。
独立的 computed 属性
与 ref 和 watch 类似,也可以使用从 Vue 导入的 computed 函数在 Vue 组件外部创建计算属性。让我们回到我们的 counter 例子:
import { ref, computed } from 'vue'const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2
在这里,computed
函数返回一个作为 computed
的第一个参数传递的 getter
类回调的输出的一个只读的响应式引用。为了访问新创建的计算变量的 value
,我们需要像使用 ref
一样使用 .value property。
让我们将搜索功能移到 setup 中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs, computed } from 'vue'// in our component
setup (props) {// 使用 `toRefs` 创建对 props 的 `user` property 的响应式引用const { user } = toRefs(props)const repositories = ref([])const getUserRepositories = async () => {// 更新 `props.user ` 到 `user.value` 访问引用值repositories.value = await fetchUserRepositories(user.value)}onMounted(getUserRepositories)// 在用户 prop 的响应式引用上设置一个侦听器watch(user, getUserRepositories)const searchQuery = ref('')const repositoriesMatchingSearchQuery = computed(() => {return repositories.value.filter(repository => repository.name.includes(searchQuery.value))})return {repositories,getUserRepositories,searchQuery,repositoriesMatchingSearchQuery}
}
对于其他的逻辑关注点我们也可以这样做,但是你可能已经在问这个问题了——这不就是把代码移到 setup
选项并使它变得非常大吗?嗯,那是真的。这就是为什么在继续其他任务之前,我们将首先将上述代码提取到一个独立的组合式函数。让我们从创建 useUserRepositories
开始:
// src/composables/useUserRepositories.jsimport { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch } from 'vue'export default function useUserRepositories(user) {const repositories = ref([])const getUserRepositories = async () => {repositories.value = await fetchUserRepositories(user.value)}onMounted(getUserRepositories)watch(user, getUserRepositories)return {repositories,getUserRepositories}
}
然后是搜索功能:
// src/composables/useRepositoryNameSearch.jsimport { ref, computed } from 'vue'export default function useRepositoryNameSearch(repositories) {const searchQuery = ref('')const repositoriesMatchingSearchQuery = computed(() => {return repositories.value.filter(repository => {return repository.name.includes(searchQuery.value)})})return {searchQuery,repositoriesMatchingSearchQuery}
}
现在在单独的文件中有了这两个功能,我们就可以开始在组件中使用它们了。以下是如何做到这一点:
// src/components/UserRepositories.vue
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import { toRefs } from 'vue'export default {components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },props: {user: { type: String }},setup (props) {const { user } = toRefs(props)const { repositories, getUserRepositories } = useUserRepositories(user)const {searchQuery,repositoriesMatchingSearchQuery} = useRepositoryNameSearch(repositories)return {// 因为我们并不关心未经过滤的仓库// 我们可以在 `repositories` 名称下暴露过滤后的结果repositories: repositoriesMatchingSearchQuery,getUserRepositories,searchQuery,}},data () {return {filters: { ... }, // 3}},computed: {filteredRepositories () { ... }, // 3},methods: {updateFilters () { ... }, // 3}
}
此时,你可能已经知道了这个练习,所以让我们跳到最后,迁移剩余的过滤功能。我们不需要深入了解实现细节,因为这不是本指南的重点。
// src/components/UserRepositories.vue
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'export default {components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },props: {user: { type: String }},setup(props) {const { user } = toRefs(props)const { repositories, getUserRepositories } = useUserRepositories(user)const {searchQuery,repositoriesMatchingSearchQuery} = useRepositoryNameSearch(repositories)const {filters,updateFilters,filteredRepositories} = useRepositoryFilters(repositoriesMatchingSearchQuery)return {// 因为我们并不关心未经过滤的仓库// 我们可以在 `repositories` 名称下暴露过滤后的结果repositories: filteredRepositories,getUserRepositories,searchQuery,filters,updateFilters}}
}
后言
创作不易,要是本文章对广大读者有那么一点点帮助 不妨三连支持一下,您的鼓励就是博主创作的动力
相关文章:

Vue3中的组合式API的详细教程和介绍
文章目录 前言介绍组合式 API 基础setup 组件选项 带 ref 的响应式变量生命周期钩子注册内部 setupwatch 响应式更改独立的 computed 属性后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:vue.js 🐱👓博主在前端…...

Java后端开发——JDBC(万字详解)
Java后端开发——JDBC(万字详解) 今日目标 掌握JDBC的的CRUD理解JDBC中各个对象的作用掌握Druid的使用 1,JDBC概述 在开发中我们使用的是java语言,那么势必要通过java语言操作数据库中的数据。这就是接下来要学习的JDBC。 1.1 …...

python etree.HTML 以及xpath 解析网页的工具
文章目录 导入模块相关语法实战 导入模块 from lxml import etree相关语法 XPath(XML Path Language)是一种用于在XML文档中定位和选择元素的语言。XPath的主要应用领域是在XML文档中进行导航和查询,通常用于在XML中选择节点或节点集合。以…...

电机伺服驱动学习笔记(7)待编辑
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么?二、使用步骤1.引入库2.读入数据 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:…...

【云备份】业务处理
文章目录 1. 业务处理作用功能 2. 代码框架编写构造函数UpLoad ——文件上传请求ListShow —— 展示页面请求处理实现Download —— 下载请求的处理实现断点续传实现 1. 业务处理 作用 业务处理模块是对客户端的业务请求进行处理 功能 1.文件上传请求:备份客户端…...

JVM GC算法
一, 垃圾回收分类: 按线程数分,可以分为串行垃圾回收器和并行垃圾回收器。 按工作模式分,可以分为并发垃圾回收器和独占式垃圾回收器 按碎片处理方式分,可以分为压缩式垃圾回收器和非压缩式垃圾回收器按工作的内存区间分,又可分为…...

对Spring框架的一些总结
对Spring框架的一些总结 在文章开头我真心推荐大家一个优秀的编程老师:孙帅老师(孙哥suns),孙帅老师在哔哩哔哩的Spring5教学视频时长接近33个小时,从0基础到一步一步手把手的教你抽丝剥茧分析Spring框架的所有知识,孙帅老师的教…...

C# WPF上位机开发(第一个应用)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 万事开头难,很多事情都是难在第一步。走出了这第一步,回过头看以前走的每一步,发现其实也不难。用c# wpf编写界…...

有点迷糊class和初始化参数的用法了
翻阅手册https://www.runoob.com/python3/python3-class.html Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。 如果你以前没有接触过面向对象的编程语言&…...

如何选择一款安全稳定的跨境浏览器?
选择适合自己的跨境浏览器是进行跨境电商和跨境交流的关键一步。本文将为您介绍如何客观地选择一款安全稳定的跨境浏览器,以便更好地进行跨境业务。 在选择跨境浏览器时,以下几个因素是需要考虑的: 网络速度:跨境业务需要稳定而高…...

SQL Server 数据库,使用函数查询统计信息
4.1 在查询中使用函数 在前面章节已经学习了一些简单的增、删、改、查询的T-SOL.语句,但是为了更方便快捷地完 成大量的任务,SOLServer提供了一些内部函数,可以和SOLServer的SELECT语句联合使用,也可 以与UPDATE和INSERT一起使用&…...

mysql区分大小写吗
mysql在windows下默认是不区分大小写的,在linux下默认是区分大小写的。 所以,为了避免出问题,许多公司的数据库编程规范中明确规定:库名、表名、列名、索引名一律小写,不同单词之间以下划线分割,且控制在3…...

HarmonyOS 开发案例分享:万能卡片也能用来玩游戏
一、前言 作为一名开发爱好者,从大了讲,我学习并进行 HarmonyOS 相关开发是为了能为鸿蒙生态建设尽一份绵薄之力,从小了讲,就是为了自己的兴趣。而万能卡片是一个让我非常感兴趣的东西。 很多时候我跟别人解释什么是万能卡片&…...

Could NOT find resource [logback-test.xml]
修改 之后就可以正常启动了...

11.28 C++作业
提示并输入一个字符串,统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数 要求使用C风格字符串完成 #include <iostream>using namespace std;int main() {string str;cout << "请输入一个字符串:" <<…...

126. 单词接龙 II
126. 单词接龙 II 需要注意的是,由于要找最短路径,连接 dot 与 lot 之间的边就不可以被记录下来,同理连接 dog 与 log 之间的边也不可以被记录。这是因为经过它们的边一定不会是最短路径。因此在广度优先遍历的时候,需要记录的图…...

SpringBoot+SSM项目实战 苍穹外卖(2)
继续上一节的内容,本节完成新增员工、员工分页查询、启用禁用员工账号、编辑员工、导入分类模块功能代码。 目录 新增员工(完整流程分为以下五个部分)需求分析和设计代码开发功能测试代码完善 (ThreadLocal 线程局部变量)代码提交 员工分页查询代码完善 扩展Spring …...

vue常见优化手段
永远不要过早优化 why?过早优化的代价就是开发时间变长,开发成本增加,它会慢慢的让我们的代码变得不可阅读,难以维护;这些都是优化带来的代价。有句话是这样说的:命运馈赠的礼物,早已在暗中标好…...

vue3通过v-model实现父子组件通信
单一值传递 父组件 <template><div ><h1>v-model实现父子组件通讯</h1><hr><child1 v-model"num"></child1><!-- 上下两个是等价的 --><child1 :modelValue"num" update:modelValue"handle&quo…...

java设计模式学习之【桥接模式】
文章目录 引言桥接模式简介定义与用途:实现方式 使用场景优势与劣势桥接模式在Spring中的应用绘图示例代码地址 引言 想象你正在开发一个图形界面应用程序,需要支持多种不同的窗口操作系统。如果每个系统都需要写一套代码,那将是多么繁琐&am…...

prometheus|云原生|kubernetes内部安装prometheus
架构说明: prometheus是云原生系统内的事实上的监控标准,而kubernetes集群内部自然还是需要就地取材的部署prometheus服务了 那么,prometheus-server部署的方式其实是非常多的,比如,kubesphere集成方式,h…...

利用Python中的Manim进行数学绘画和创作
相信很多同学就算没听过3Blue1Brown,也一定曾看过他们出品的视频,其从独特的视觉角度解说各种数学概念,内容包括线性代数、微积分、神经网络、傅里叶变换以及四元数等晦涩难懂的知识点。例如最火的《线性代数本质》系列视频。 那么这些视频是…...

Uniapp
UniApp是一个强大的跨平台应用开发框架 随着移动互联网的快速发展,跨平台应用开发成为了一个重要的需求。UniApp就是一个能够满足这一需求的强大框架。本文将介绍UniApp的基本概念、优势、使用方法和未来发展。 一、UniApp概述 UniApp是一个基于Vue.js开发的跨平…...

HNU-青蛙与蚊子
【问题描述】 有 n 只青蛙位于坐标轴 OX 上,对于每只青蛙,有两个已知值 xi、ti,表示第 i 只青蛙在坐标的位置(各不相同)以及它的舌头的长度。同样有 m 只蚊子一只接一只的落到坐标轴上,对于每只蚊子&#x…...

【新论文】【模型攻击】DiffAttack 针对基于扩散的对抗性净化的逃避攻击
DiffAttack: Evasion Attacks Against Diffusion-Based Adversarial Purification 作者: Mintong Kang; Dawn Song; Bo Li 链接: http://arxiv.org/pdf/2311.16124v1 备注: Accepted to NeurIPS 2023 摘要: 基于扩散的净化防御利用扩散模型去除对抗样本的精心设计的扰动&#…...

【Redis缓存】RedisTemplate如何获取符合要求的key,批量获取key
RedisTemplate如何获取符合要求的key,批量获取key 一、方法/命令二、数据使用 一、方法/命令 如果使用命令的形式,输入以下命令即可 keys *如果使用RedisTemplate,则方法为 redisTemplate.keys()获取所有符合条件的key。 二、数据使用 redis中缓存了…...

springboot核心原理之@SpringbootApplication
1.SpringbootApplication Configuration标志的类 在spring ioc启动的时候就会加载创建这个类对象 EnableAutoConfiguration 中有两个注解 (1)AutoConfigurationPackage 扫描主程序包(主程序main所在包及其子包) 可以看到这个类 : static c…...

阻抗匹配电阻原理及其应用
一、匹配电阻的作用 1、阻抗匹配 当信号频率比较高,上升沿比较陡时,电子信号经过阻抗不同的地方时也会产设反射。 PCB的单线阻抗一般会设计成50Ω,发射端阻抗一般是17到40,而接收端一般是MOS管的输入,阻抗是比较大的…...

IDEA2023安装教程(超详细)
文章目录 前言安装IntelliJ IDEA1. 下载IntelliJ IDEA2. 运行安装程序3. 选择安装路径4. 选择启动器设置5. 等待安装完成6. 启动IntelliJ IDEA7. 配置和设置8. 激活或选择许可证9. 开始使用 总结 前言 随着软件开发的不断发展,IntelliJ IDEA成为了许多开发人员首选…...

【MySql】悲观锁和乐观锁的介绍
一、并发控制 当程序中可能出现并发的情况时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这就叫做并发控制。并发控制的目的是保证一个用户的工作不会对另一个用…...