vue3-组合式 API

news2024/11/26 15:42:29

什么是组合式 API?

组合式 API (Composition API) 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件。它是一个概括性的术语,涵盖了以下方面的 API:

  • 响应式 API:例如 ref() 和 reactive(),使我们可以直接创建响应式状态、计算属性和侦听器。

  • 生命周期钩子:例如 onMounted() 和 onUnmounted(),使我们可以在组件各个生命周期阶段添加逻辑。

  • 依赖注入:例如 provide() 和 inject(),使我们可以在使用响应式 API 时,利用 Vue 的依赖注入系统。

组合式 API 是 Vue 3 及 Vue 2.7 的内置功能。对于更老的 Vue 2 版本,可以使用官方维护的插件 @vue/composition-api。在 Vue 3 中,组合式 API 基本上都会配合 <script setup> 语法在单文件组件中使用。下面是一个使用组合式 API 的组件示例:

<script setup>
import { ref, onMounted } from 'vue'

// 响应式状态
const count = ref(0)

// 更改状态、触发更新的函数
function increment() {
  count.value++
}

// 生命周期钩子
onMounted(() => {
  console.log(`计数器初始值为 ${count.value}。`)
})
</script>

<template>
  <button @click="increment">点击了:{{ count }} 次</button>
</template>

虽然这套 API 的风格是基于函数的组合,但组合式 API 并不是函数式编程。组合式 API 是以 Vue 中数据可变的、细粒度的响应性系统为基础的,而函数式编程通常强调数据不可变。

为什么要有组合式 API?

更好的逻辑复用

组合式 API 最基本的优势是它使我们能够通过组合函数来实现更加简洁高效的逻辑复用。在选项式 API 中我们主要的逻辑复用机制是 mixins,而组合式 API 解决了 mixins 的所有缺陷。

组合式 API 提供的逻辑复用能力孵化了一些非常棒的社区项目,比如 VueUse,一个不断成长的工具型组合式函数集合。组合式 API 还为其他第三方状态管理库与 Vue 的响应式系统之间的集成提供了一套简洁清晰的机制,例如不可变数据、状态机与 RxJS。

更灵活的代码组织

许多用户喜欢选项式 API 的原因是它在默认情况下就能够让人写出有组织的代码:大部分代码都自然地被放进了对应的选项里。然而,选项式 API 在单个组件的逻辑复杂到一定程度时,会面临一些无法忽视的限制。这些限制主要体现在需要处理多个逻辑关注点的组件中,这是我们在许多 Vue 2 的实际案例中所观察到的。

我们以 Vue CLI GUI 中的文件浏览器组件为例:这个组件承担了以下几个逻辑关注点:

  • 追踪当前文件夹的状态,展示其内容

  • 处理文件夹的相关操作 (打开、关闭和刷新)

  • 支持创建新文件夹

  • 可以切换到只展示收藏的文件夹

  • 可以开启对隐藏文件夹的展示

  • 处理当前工作目录中的变更 这个组件最原始的版本是由选项式 API 写成的。如果我们为相同的逻辑关注点标上一种颜色,那将会是这样:

你可以看到,处理相同逻辑关注点的代码被强制拆分在了不同的选项中,位于文件的不同部分。在一个几百行的大组件中,要读懂代码中的一个逻辑关注点,需要在文件中反复上下滚动,这并不理想。另外,如果我们想要将一个逻辑关注点抽取重构到一个可复用的工具函数中,需要从文件的多个不同部分找到所需的正确片段。

而如果用组合式 API 重构这个组件,将会变成下面右边这样:

现在与同一个逻辑关注点相关的代码被归为了一组:我们无需再为了一个逻辑关注点在不同的选项块间来回滚动切换。此外,我们现在可以很轻松地将这一组代码移动到一个外部文件中,不再需要为了抽象而重新组织代码,大大降低了重构成本,这在长期维护的大型项目中非常关键。

更好的类型推导

近几年来,越来越多的开发者开始使用 TypeScript 书写更健壮可靠的代码,TypeScript 还提供了非常好的 IDE 开发支持。然而选项式 API 是在 2013 年被设计出来的,那时并没有把类型推导考虑进去,因此我们不得不做了一些复杂到夸张的类型体操才实现了对选项式 API 的类型推导。但尽管做了这么多的努力,选项式 API 的类型推导在处理 mixins 和依赖注入类型时依然不甚理想。

因此,很多想要搭配 TS 使用 Vue 的开发者采用了由 vue-class-component 提供的 Class API。然而,基于 Class 的 API 非常依赖 ES 装饰器,在 2019 年我们开始开发 Vue 3 时,它仍是一个仅处于 stage 2 的语言功能。我们认为基于一个不稳定的语言提案去设计框架的核心 API 风险实在太大了,因此没有继续向 Class API 的方向发展。在那之后装饰器提案果然又发生了很大的变动,在 2022 年才终于到达 stage 3。另一个问题是,基于 Class 的 API 和选项式 API 在逻辑复用和代码组织方面存在相同的限制。

相比之下,组合式 API 主要利用基本的变量和函数,它们本身就是类型友好的。用组合式 API 重写的代码可以享受到完整的类型推导,不需要书写太多类型标注。大多数时候,用 TypeScript 书写的组合式 API 代码和用 JavaScript 写都差不太多!这也让许多纯 JavaScript 用户也能从 IDE 中享受到部分类型推导功能。

更小的生产包体积

搭配 <script setup> 使用组合式 API 比等价情况下的选项式 API 更高效,对代码压缩也更友好。这是由于 <script setup> 形式书写的组件模板被编译为了一个内联函数,和 <script setup> 中的代码位于同一作用域。不像选项式 API 需要依赖 this 上下文对象访问属性,被编译的模板可以直接访问 <script setup> 中定义的变量,无需从实例中代理。这对代码压缩更友好,因为本地变量的名字可以被压缩,但对象的属性名则不能。

与选项式 API 的关系

取舍

一些从选项式 API 迁移来的用户发现,他们的组合式 API 代码缺乏组织性,并得出了组合式 API 在代码组织方面“更糟糕”的结论。我们建议持有这类观点的用户换个角度思考这个问题。

组合式 API 不像选项式 API 那样会手把手教你该把代码放在哪里。但反过来,它却让你可以像编写普通的 JavaScript 那样来编写组件代码。这意味着你能够,并且应该在写组合式 API 的代码时也运用上所有普通 JavaScript 代码组织的最佳实践。如果你可以编写组织良好的 JavaScript,你也应该有能力编写组织良好的组合式 API 代码。

选项式 API 确实允许你在编写组件代码时“少思考”,这是许多用户喜欢它的原因。然而,在减少费神思考的同时,它也将你锁定在规定的代码组织模式中,没有摆脱的余地,这会导致在更大规模的项目中难以进行重构或提高代码质量。在这方面,组合式 API 提供了更好的长期可维护性。

组合式 API 是否覆盖了所有场景?

组合式 API 能够覆盖所有状态逻辑方面的需求。除此之外,只需要用到一小部分选项:props,emits,name 和 inheritAttrs。

从 3.3 开始你可以直接通过 <script setup> 中的 defineOptions 来设置组件名或 inheritAttrs 属性。

如果你在代码中只使用了组合式 API (以及上述必需的选项),那么你可以通过配置编译时标记来去掉 Vue 运行时中针对选项式 API 支持的代码,从而减小生产包大概几 kb 左右的体积。注意这个配置也会影响你依赖中的 Vue 组件。

可以在同一个组件中使用两种 API 吗?

可以。你可以在一个选项式 API 的组件中通过 setup() 选项来使用组合式 API。

然而,我们只推荐你在一个已经基于选项式 API 开发了很久、但又需要和基于组合式 API 的新代码或是第三方库整合的项目中这样做。

选项式 API 会被废弃吗?

不会,我们没有任何计划这样做。选项式 API 也是 Vue 不可分割的一部分,也有很多开发者喜欢它。我们也意识到组合式 API 更适用于大型的项目,而对于中小型项目来说选项式 API 仍然是一个不错的选择。

与 Class API 的关系

我们不再推荐在 Vue 3 中使用 Class API,因为组合式 API 提供了很好的 TypeScript 集成,并具有额外的逻辑重用和代码组织优势。

和 React Hooks 的对比

组合式 API 提供了和 React Hooks 相同级别的逻辑组织能力,但它们之间有着一些重要的区别。

React Hooks 在组件每次更新时都会重新调用。这就产生了一些即使是经验丰富的 React 开发者也会感到困惑的问题。这也带来了一些性能问题,并且相当影响开发体验。例如:

  • Hooks 有严格的调用顺序,并不可以写在条件分支中。

  • React 组件中定义的变量会被一个钩子函数闭包捕获,若开发者传递了错误的依赖数组,它会变得“过期”。这导致了 React 开发者非常依赖 ESLint 规则以确保传递了正确的依赖,然而,这些规则往往不够智能,保持正确的代价过高,在一些边缘情况时会遇到令人头疼的、不必要的报错信息。

  • 昂贵的计算需要使用 useMemo,这也需要传入正确的依赖数组。

  • 在默认情况下,传递给子组件的事件处理函数会导致子组件进行不必要的更新。子组件默认更新,并需要显式的调用 useCallback 作优化。这个优化同样需要正确的依赖数组,并且几乎在任何时候都需要。忽视这一点会导致默认情况下对应用进行过度渲染,并可能在不知不觉中导致性能问题。

  • 要解决变量闭包导致的问题,再结合并发功能,使得很难推理出一段钩子代码是什么时候运行的,并且很不好处理需要在多次渲染间保持引用 (通过 useRef) 的可变状态。

相比起来,Vue 的组合式 API:

  • 仅调用 setup() 或 <script setup> 的代码一次。这使得代码更符合日常 JavaScript 的直觉,不需要担心闭包变量的问题。组合式 API 也并不限制调用顺序,还可以有条件地进行调用。

  • Vue 的响应性系统运行时会自动收集计算属性和侦听器的依赖,因此无需手动声明依赖。

  • 无需手动缓存回调函数来避免不必要的组件更新。Vue 细粒度的响应性系统能够确保在绝大部分情况下组件仅执行必要的更新。对 Vue 开发者来说几乎不怎么需要对子组件更新进行手动优化。

我们承认 React Hooks 的创造性,它是组合式 API 的一个主要灵感来源。然而,它的设计也确实存在上面提到的问题,而 Vue 的响应性模型恰好提供了一种解决这些问题的方法。

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

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

相关文章

TCP_IP(6)

网络层 在复杂的网络环境中确定一个合适的路径. IP协议 与TCP协议并列,都是网络体系中最核心的协议. 基本概念 主机:配有IP地址,但是不进行路由控制的设备; 路由器:即配有IP地址,又能进行路由控制; 节点:主机和路由器的统称; 协议头格式 4位版本号(version):指定IP协议的版…

python 人脸检测器

import cv2# 加载人脸检测器 关键文件 haarcascade_frontalface_default.xml face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml)# 读取图像 分析图片 ren4.png image cv2.imread(ren4.png) gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 进行人脸…

Unity类银河恶魔城学习记录7-5 p71 Improving sword throwing state源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Sword_Skill.cs using System.Collections; using System.Collections.Ge…

基于Springboot+Vue实现的宿舍管理系统

基于SpringbootVue的宿舍管理系统 1.系统相关性介绍1.1 系统架构1.2 设计思路 2.功能模块介绍2.1 用户信息模块2.2 宿舍管理模块2.3 信息管理模块 3. 源码获取以及远程部署 前言&#xff1a; 在现代教育环境中&#xff0c;学生宿舍的管理显得尤为重要&#xff0c;需要一套能…

第五篇【传奇开心果系列】Python微项目技术点案例示例:中文有声故事书

传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目目标和背景二、雏形示例代码三、扩展思路四、用户自定义输入示例代码五、故事选择示例代码六、语音控制示例代码七、播放控制示例代码八、文本转换语音示例代码九、微项目雏形核心部分示例代…

情人节到了,写一份爱心程序(python)

前言 情人节到了&#xff0c;写一份爱心代码给喜欢的人呀 公式 首先我们介绍下爱心的公式的参数方程&#xff1a; x 16 s i n 3 ( t ) x 16sin^3(t) x16sin3(t) y 13 c o s ( t ) − 5 c o s ( 2 t ) − 2 c o s ( 3 t ) − c o s ( 4 t ) y 13cos(t) - 5cos(2t) - 2co…

K8S集群实践之十:虚拟机部署阶段性总结

目录 1. 说明&#xff1a; 2. 安装准备 2.1 每个节点设置双网卡&#xff0c;一卡做网桥&#xff08;外部访问&#xff09;&#xff0c;一卡做NAT&#xff08;集群内网访问&#xff09; 2.2 准备一个可用的代理服务器 3. 由于虚拟机崩溃&#xff08;停电&#xff0c;宿主机…

谁拿了最多奖学金——NOIP 2005 提高组

输入样例&#xff1a; 4 YaoLin 87 82 Y N 0 ChenRuiyi 88 78 N Y 1 LiXin 92 88 N N 0 ZhangQin 83 87 Y N 1 输出样例&#xff1a; ChenRuiyi 9000 28700 这道题用结构体做对吧 #include <bits/stdc.h> using namespace std; class student{public:string name;int FG…

微信小程序框架阐述

目录 一、框架 响应的数据绑定 页面管理 基础组件 丰富的 API 二、逻辑层 App Service 小程序的生命周期 注册页面 使用 Page 构造器注册页面 在页面中使用 behaviors 使用 Component 构造器构造页面 页面的生命周期 页面路由 页面栈 路由方式 注意事项 模块化…

Java的异常体系

一、体系简介 java中的Exception类的子类不仅仅只是像上图所示只包含IOException和RuntimeException这两大类&#xff0c;事实上Exception的子类很多很多&#xff0c;主要可概括为&#xff1a;运行时异常与非运行时异常。 在上述体系中&#xff0c;Error表示严重的系统错误&am…

C++面向对象程序设计-北京大学-郭炜【课程笔记(二)】

C面向对象程序设计-北京大学-郭炜【课程笔记&#xff08;二&#xff09;】 1、结构化程序设计结构化程序设计的不足 2、面向对象的程序设计2.1、面向对象的程序设计2.2、从客观事物抽象出类2.3、对象的内存分配2.4、对象之间的运算2.5、使用类的成员变量和成员函数用法1&#x…

optee UTA加载

流程 动态TA按照存储位置的不同分为REE filesystem TA&#xff1a;存放在REE侧文件系统里的TA&#xff1b; Early TA&#xff1a;被嵌入到optee os里的在supplicant启动之前就可用了。 这里我们讲的是常规的存放在REE侧文件系统里的TA。 通过GP标准调用的与TA通信的命令(opens…

C语言学习day14:数组定义和使用

定义变量&#xff1a; 数据类型 变量 值 数组定义&#xff1a; 数据类型 数组名[元素个数]{值1,值2,值3} 代码&#xff1a; int main() {//定义变量//数据类型 变量 值//数组定义//数据类型 数组名[元素个数]{值1,值2,值3}//数组下标 数组名[小标]//数组下标是…

Java学习第十五节之回顾方法的调用

方法的调用 package oop;public class Demo03 {public static void main(String[] args) {//实际参数和形式参数的类型要对应&#xff01;int add Demo03.add(1,2);System.out.println(add);}public static int add(int a,int b){return ab;}}package oop;//值传递 public cl…

列表推导式与生成表达式的区别

列表推导式与生成式表达式的区别&#xff1a; 列表推导式 res[i for i in range(6)] print(res) 结果&#xff1a; [0, 1, 2, 3, 4, 5] 生成表达式&#xff1a; res(i for i in range(6)) print(res) 结果&#xff1a; <generator object <genexpr> at 0x0000013EAD0…

mathtype公式

Mathtype 手写板 Win11手写板按钮灰色问题解决&#xff1a;在C:\Program Files\Common Files\microsoft shared\ink目录下粘贴mip.exe&#xff0c;C:\Program Files\Common Files\microsoft shared\ink\en-US目录下添加mip.exe.mui提取码y04v 公式识别 配合免费图片公式识别…

【MySQL】:DQL查询

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. DQL1.1 基本语法1.2 基础查询1.3 条件查询1.3 聚合函数 &#x1f324;️ 全篇…

IO流---字节输入输出流,字符输入输出流

IO流概述 IO流&#xff0c;即输入输出流&#xff08;Input Output Stream&#xff09;&#xff0c;是一种抽象概念&#xff0c;用来处理设备之间的数据传输问题&#xff0c;例如文件复制、文件上传、文件下载等。在Java中&#xff0c;对数据的操作通常是通过流的方式进行的&…

qml中解决Page控件头部元素Margin不生效的问题

0、想要的效果 1、问题描述 经测试&#xff1a;Page的头部无法完美的进行左右边距设置&#xff0c;leftMargin可以&#xff0c;rightMargin不可以。。。。 Page {// ...header: Frame {id: headerheight: 70// 必须首先锚定位&#xff0c;然后设置边距才生效padding: 0anchor…

轻松掌握Jenkins执行远程window的Jmeter接口脚本

Windows环境&#xff1a;10.1.2.78 新建与配置节点 【系统管理】—【管理节点】—【新建节点】输入节点名称&#xff0c;勾选“dumb slave”&#xff0c;点击ok 按如上配置&#xff1a; 说明&#xff1a; Name&#xff1a;定义slave的唯一名称标识&#xff0c;可以是任意字…