Vue3.0 组合式 API 分析与实践

2023-11-15

本文带大家深入理解组合式 API 的设计详情,同时加入我们的实践经验总结。

01 背景

Vue3.x 版本的出现带来了许多令人眼前一亮的新特性,其中组合式 API(Composition API),一组附加的、基于功能的 API 被作为一种新的逻辑复用和代码组织的方式提供给了开发者,提供更加灵活的组合组件逻辑能力。同时组合式 API 通过使用简单的变量和函数,也提供了更好的类型推断,这使得通过新的 API 编写的代码即使不用 TypeScript 也可以通过 IDE 的支持方便的获得类型提示。组合式 API 具体的设计动机和详情可以参考它的 RFC。本文带大家深入理解组合式 API 的设计详情,同时加入我们的实践经验总结。

02 简介

组合式 API (Composition API)

作为最重要的一项变动,Vue3.x 引入了组合式 API,它是一组附加的、基于功能的 API,用于灵活的组合组件逻辑。主要通过 setup 的生命周期进行组件的初始化,参考以下组合式 API 的基本使用方法:

<template>
    <button @click="increment">
        Count is: {{ state.count }}, double is: {{ state.double }}
    </button>
</template>
<script>
import {reactive, computed} from 'vue';

export default {
    setup() {
        const state = reactive({
            count: 0,
            double: computed(() => state.count * 2)
        });
        function increment() {
            state.count++;
        }
        return {
            state,
            increment
        };
    }
};
</script>

组合式 API 全景
组合式 API 的使用,官方参考文档和 API 文档有详细说明,这里不过多说明。为方便大家理解和把握,根据官方的 API 文档整理了全景图。
在这里插入图片描述

Vue 组合式 API 可以分为五大块,涉及共计 31 个:

  • 数据响应(复杂对象):响应性 API 基础,支持复杂对象 Object、Array 的数据响应;
  • 数据响应(内部值):内部值指 JS 内置的简单数据结构,包括 String、Number 等;
  • computed 和 watch:基于 Ref 的数据计算与监听;
  • 生命周期:对原生命周期封装;
  • 其他 API:重要支持性 API;

03 设计分析

设计动机

逻辑复用与代码组织

Vue 的优点在于能够简单快速的搭建中小型应用项目。然而随着 Vue 如今生态的快速发展,项目的规格也变得越来越大。而受限于 Vue 当前的 API 的设计模式,不同开发者在维护同一个项目的时候,以下问题就显露出来了:

  1. 随着功能的逐渐丰富,复杂组件的代码变得愈发难以理解,尤其是当开发者在阅读其他人编写的代码的时候。因为 Vue 现存的 API使得代码通过 options 的方式来组织,相同逻辑的代码分散到不同的 options里,不符合就近原则,因此通过逻辑上就近的原则来考虑来组织代码更加合理;
  2. 多个组件之间缺乏优雅、低成本复用逻辑的机制;现在的 Mixin 混合组件逻辑的方式,存在来源不清晰的问题;

而组合式 API可以更加灵活的组织组件代码,不同于OptionsAPI,代码可以通过特定功能来组织,也提供了组件间或者组件之外更加直接的提取、复用逻辑的能力。

更好的类型推断

支持大型项目开发者对 TypeScript 的需求。然而 Vue 现存 API 最初的设计并没有考虑类型推断,大部分原因是由于 Vue 依赖 this 上下文的方式暴露属性,这与原生 JS 中的 this 大相径庭,而这也对当前 Vue 与 TypeScript 的整合造成了极大的困难。

目前许多 Vue 的使用者利用 vue-class-component 通过修饰符将组件以类的方式进行编写。在设计 Vue3.x 阶段,Vue 团队尝试提供内置的 Class API 来解决类型的问题,不过经过反复的讨论发现这种方式必须依赖修饰符,这种方式的不稳定性和不确定性导致其成为了一个有风险的基础建设。

相比较,组合式 API 大多使用简单的变量和函数,类型更加友好,并且可以享受完整的类型推断提示。使用新的 API 编写的代码在 TypeScript 和 JavaScript 看起来几乎相同,因此即使不用 TypeScript ,也可以通过 IDE 的支持方便的获得类型提示。

代码组织

组合式 API 通过使用引入函数的方式来替代原有的 options 选项,在实例中,reactive和refs函数替代了原有的data,computed函数代替了computed属性,watchEffect函数代替了watch属性。乍一看所有逻辑都混合在一起写在了 setup() 中,代码组织还不如使用 options,但是如果我们真正考虑代码组织的最终目标 —— 更加容易的理解代码,我们会发现仅仅知道一个复杂的组件有哪些选项并不能帮助我们阅读理解代码,从而理解整个组件的代码逻辑。当开发者们阅读其他人编写的组件代码的时候,比起「组件使用了哪些选项?」,他们更在意的是「组件想要做什么?」。

我们可以通过以下例子来对比两者之间的区别,首先用 Options API 的写法完成一个组件,组件逻辑很简单,拥有两个响应式数据 name 和 gender,两个方法 getName 和 getGender,在调用时可以获取到 people 对象中的 name 和 gender 属性,代码如下所示:

const component = {
    data() {
        return {
            people: {
                name: 'maxuxiao',
                gender: 'male'
            },
            name: '',
            gender: ''
        };
    },
    methods: {
        getName() {
            this.name = this.people.name;
        },
        getGender() {
            this.gender = this.people.gender;
        }
    }
};

我们可以发现,如果把获取名字和获取性别看做两组逻辑,把代码以逻辑进行划分的话,会是这样:

相同的逻辑用相同的颜色表示。

在这里插入图片描述

当然组合式 API 的引入也存在一定的弊端,它在代码组织方面提供了更多灵活性的同时,也需要开发人员通过功能分组的的方式去降低 setup 函数的复杂度,避免 setup 代码量越来越多,return 的对象越来越复杂情况这种面条代码的产生。我们期望的是 setup 函数现在只是简单地作为调用所有组合函数的入口,参考以下功能分组的方式:

const component = {
    setup() {
        const people = {
            name: 'maxuxiao',
            gender: 'male'
        };
        return {
            ...useName(people),
            ...useGender(people)
        };
    }
};
const component2 = {
    setup() {
        const people = {
            name: 'chenmingming',
            gender: 'male'
        };
        return {
            ...useName(people),
            ...useGender(people)
        };
    }
};
// 处理 name 相关的业务
function useName(people) {
    const name = Vue.ref('');
    const getName = () => {
        name.value = people.name;
    };
    return {
        name,
        getName
    };
}
// 处理 gender 相关的业务
function useGender(people) {
    const gender = Vue.ref('');
    const getGender = () => {
        gender.value = people.gender;
    };
    return {
        gender,
        getGender
    };
}

通过以上代码我们可以看到代码被按照业务逻辑分成了多个函数,而 setup 函数负责将它们组合起来。通过这种方式也达到了组合式 API 设计的另外一个核心目的:让相同的代码逻辑在不同组件中低成本的抽取和复用。不过对 Vue3.x 来说,组合式 API 并不是默认的方案,它被定义为高级特性,意在解决大型应用程序中的复杂组件的编写。

组件逻辑复用

目前业界解决组件逻辑复用和代码组织的机制主要做法有:

  • 组合式 API:Vue
  • HOC 高阶组件:React
  • Mixin 混合:React + Vue

Mixin 不论是 Vue 还是 React,现在都不太推荐使用,主要问题是来源不清晰、容易冲突、类型推导不明确等问题。

HOC 高阶组件

可以看作 React 对装饰模式的一种实现,高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。

高阶组件(HOC)是 React 中的高级技术,用来重用组件逻辑。但高阶组件本身并不是 React API。它只是一种模式,这种模式是由 React 自身的组合性质必然产生的。

function visible(WrappedComponent) {
    return class extends Component {
        render() {
            const { visible, ...props } = this.props;
            if (visible === false) return null;
            return ;
        }
    }
}

04 业界观点

业界对组合式 API 存在一定的争议,具体内容可以参考官方的讨论:基于功能的组件 API 和基于函数的组件 API(扩展讨论)。我在这里帮大家提炼总结下文章的观点,大家担心的是:

  • 原来的 options API 按
    props、computed、watch、methods、生命周期等进行分组,存在一定约束,就算是低级别的程序员,也不会写出太杂乱无章的代码。但组合式
    API,没有这些约束,容易导致『意大利面条式』代码。

在这里插入图片描述

尤大对这个问题进行详细的回复,他的观点,组合式 API 用于解决:

  • 复杂大型的组件复用问题;
  • 共享逻辑的组件 (Mixin,HOC 高阶组件,slot);

尤大同时承认可能导致意大利面条代码,他建议通过代码规范 / 指南 / CR 等方式解决。尤大没有说过让大家放弃使用 option API,只是说明了初衷和要解决的问题,暂时官方没有提供最佳实践。

05 项目实践

那么业界大家对组合式 API 落地情况怎么样呢?我们调研主流的几个支持 Vue3.0 的组件库,对组合式 API 落地使用情况分析:

  • elment-plus:全部组件使用组合式 API,未使用 Options API
  • vant:全部组件使用组合式 API,未使用 Options API

同时我们也注意到部分组件的 setup 函数,代码量非常大,组件里把原来的 props、computed、watch、methods、生命周期都放在 setup 中,这显然是不合适的做法,也进一步应证了『意大利面条』的说法。

由于现阶段 Vue 团队没有发布官方的最佳实践,所以项目中是否应该使用组合式 API 来替代 Options API 就需要结合实际项目情况来考虑。在实际业务项目中,对于没有涉及到大型应用、复杂组件以及 TS 支持的情况,组合式 API 的使用也就不是那么必要。

经过实践尝试,结合 Vue 社区内的讨论结果,我们决定当遇到以下几种情况的时候,使用组合式 API,其他情况继续保持使用 Options API:

  1. 逻辑复杂的大型非业务组件;
  2. 存在大量共享逻辑的组件,包含 mixin、HOC 高阶组件、slot 等场景;
  3. 组件涉及到多种Options或生命周期钩子函数;

06 总结

经过实践,组合式 API 的出现从根本上解决了 Vue 在逻辑复用以及代码组织上存在的问题,同时也在类型推断上有了更好的支持,这意味着用组合式 API 编写的代码可以享受完整的类型推断。另外 Vue3 也引入了 tree shaking 特性,这种按需引用 API 的使用方式可以在编译阶段将没有用到的代码进行 tree shaking 优化,从而有效减小项目打包体积。

同时,经过实际项目实践,组合式 API 的引入确实解决了 Options API 在代码组织上存在的问题,体现在:

  • 【简洁性】极大的提升了代码的可读性和可维护性,尤其在开发复杂逻辑组件或者组件涉及到大量 Options 以及多种生命周期钩子的场景;
  • 【逻辑复用】根本上解决了通过 mixin 方案复用逻辑带来的隐式依赖,命名冲突等问题;
  • 【类型推导】入口提供的 setup 函数中,开发者也不用再依赖 this 上下文的方式暴露属性,代码书写风格上也是更加精简,可以支持TypeScript 的类型推导;

另外,组合式 API 虽然提供了更加灵活的代码组织能力,但是缺乏经验的开发者对组合式API的滥用会使得代码更加晦涩难懂。Options API 通过约定我们该在哪个位置做什么事,一定程度上也强制我们进行了代码分隔。而没有正确进行逻辑分隔的组合式 API 会使 setup 中的代码量越来越多,导致「意大利面条代码」情况的出现。总的来说,组合式 API 在提升了代码质量上限的同时,也降低了下限。

文章看完,还不过瘾?
更多精彩内容欢迎关注百度开发者中心公众号

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue3.0 组合式 API 分析与实践 的相关文章

随机推荐

  • Unity中物体跟随另一个物体移动,且他们不是父子关系

    更新啦 我找到解决办法啦 经过我做出的无数的无用功之后 终于在学长的指导下找到了解决办法 之前说的问题如下 在一个项目中想要如题目要求的效果 A随着B位置发生变化很好做 就是 A transform position Vector3 Mov
  • 音视频开发(40)---麦克风阵列声源定位 GCC-PHAT

    麦克风阵列声源定位 GCC PHAT 版权声明 本文为博主原创文章 未经博主允许不得转载 https blog csdn net u010592995 article details 79735198 麦克风阵列声源定位 一 利用麦克风阵列
  • 数据校验(CRC 原理、LRC、奇偶校验、校验和)

    数据校验 数据在传输的过程中 会受到各种干扰的影响 如脉冲干扰 随机噪声干扰和人为干扰等 这会使数据产生差错 为了能够控制传输过程的差错 通信系统必须采用有效措施来控制差错的产生 并保证数据的完整性 如下所示的传输错误 奇偶校验 奇偶校验是
  • 使用django的APP在前端上传excel通过post传给后端读取并打印

    文章目录 前言 前端 后端 前言 备研了 博客许久未更了 但是学期末的大作业 遇到了问题并成功解决了 不得不记录一下 前端
  • 关系数据库的由来

    关系数据库的由来 1970年 IBM的研究员E F Codd博士在刊物 Communication of the ACM 上发表了一篇名为 A Relational Model of Data for Large Shared Data B
  • vscode跳转到定义的问题

    vscode php怎么设置跳转到定义的搜索范围 我的现在只能跳转到当前文件所在项目内的定义处 我希望能直接在工作区内所有文件中查找函数的定义
  • Ubuntu搭建SMB服务器,并共享文件夹

    一 Ubuntu安装步骤 1 ubuntu安装samba sudo apt get install samba 2 创建文件夹 home share 并修改权限为777 mkdir home share chmod 777 home sha
  • STM32 GPIO速度,模式等理解

    一 GPIO模式配置 1 输入 输出模式 参考stm32手册 2 GPIO输出模式下 几种速度的区别 1 GPIO 引脚速度 GPIO Speed 2MHz 10MHz 50MHz 又称输出驱动电路的响应速度 芯片内部在I O口的输出部分安
  • EAS BOS后端代码调用BOTP生成下游单据(备忘)

    BOS开发再服务端调用BOTP生成目标单据 记录下 自用备忘 Override protected void audit Context ctx IObjectValue model throws BOSException try this
  • 0-1背包问题(双限制条件)

    给定n种物品和一个背包 物品i的重量是wi 体积是bi 其价值为vi 背包的容量为c 容积为d 问应如何选择装入背包中的物品 使得装入背包中物品的总价值最大 在选择装入背包的物品时 对每种物品只有两个选择 装入或不装入 且不能重复装入 输入
  • 华为校招机试 - 发广播(Java)

    题目描述 某地有N个广播站 站点之间有些有连接 有些没有 有连接的站点在接受到广播后会互相发送 给定一个N N的二维数组matrix 数组的元素都是字符 0 或者 1 matrix i j 1 代表i和j站点之间有连接 matrix i j
  • 使用R语言和LSTM实现时间序列异常检测

    目录 引言 长短期记忆网络 LSTM 时间序列异常检测的步骤 准备工作 数据获取
  • ES6 Iterator

    不同数据集合怎么用统一的方式读取 可以用for of循环了 转载于 https www cnblogs com moneyss p 10641507 html
  • 双向链表,单向链表//循环

    head h ifndef HEAD H define HEAD H include
  • 将Oracle zip安装文件转换成Linux可用的iso镜像

    在Oracle官网下载得到的zip安装文件 要在VMware虚拟机上挂载iso安装Oracle 所以想将zip文件转化为iso 除了使用UltraISO软件外 这里尝试使用Linux mkisofs命令生成ISO 9660文件 mkisof
  • IOS 网络初探(一) - NSURLConnection

    在IOS中 除了最基本的socket外 苹果提供了NSURLConnection类来实现网络通信 请求服务器数据 GET方式 请求服务器数据分成异步和同步两种方式 先来看看异步 非阻塞 NSURL url NSURL URLWithStri
  • LPDDR4 JEDEC标准测试实例解析--写操作

    在LPDDR4的JEDEC标准中 写操作相关的时序参数要求基本都是围绕DQS信号 如下图所示 图中的单位 tCK 指的是时钟信号的工作周期 tCK的测试方法如下图所示 Write preamble and postamble设置 这一步对于
  • 3.1 Git 分支 - 分支简介

    3 1 Git 分支 分支简介 版本说明 版本 作者 日期 备注 0 1 loon 2019 3 23 初稿 目录 文章目录 3 1 Git 分支 分支简介 版本说明 目录 1 分支简介 Figure 9 首次提交对象及其树结构 Figur
  • Pycharm

    运行该程序 窗口底部会出现终端面板 Terminal pane 显示你的代码输出结果 使用Pycharm在运行过程中 查看每个变量的操作 show variables pytharm能不能像MATLAB一样显示中间变量的值呢 答案是可以的
  • Vue3.0 组合式 API 分析与实践

    本文带大家深入理解组合式 API 的设计详情 同时加入我们的实践经验总结 01 背景 Vue3 x 版本的出现带来了许多令人眼前一亮的新特性 其中组合式 API Composition API 一组附加的 基于功能的 API 被作为一种新的