本文带大家深入理解组合式 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 在提升了代码质量上限的同时,也降低了下限。

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

Vue3.0 组合式 API 分析与实践相关推荐

  1. vue3.0 组合式API 加加减减

    通过cdn安装vue setup() createApp() ref <!DOCTYPE html> <html lang="en"> <head&g ...

  2. vue3.0响应式源码实践,vue3.0初体验

    vue3.0响应式源码实践,vue3.0初体验 镇楼图--杀生丸.jpg vue-next(vue3.0预体验) 1. 使用: 2.vue-next的目录结构 3. reactive内部实现 作者上篇 ...

  3. Vue3为什么要使用组合式API——Vue3的组合式API和Vue2的选项式API的优缺点

    Vue3为什么要使用组合式API--Vue3的组合式API和Vue2的选项式API的优缺点 1.Vue2为什么使用选项式API 2.Vue2如何使用组合式API 3.组合式API Vs 选项式API ...

  4. vue3.0 Composition API 上手初体验 使用 vue-router 构建多页面应用

    vue3.0 Composition API 上手初体验 使用 vue-router 构建多页面应用 前两讲,我们已经顺利的使用 vue3.0 将项目跑起来了.但是实在是过于简陋,目前我们几乎不可能开 ...

  5. Vue3基础-组合式 API 基础之setup

    背景 Vue3 提供了组合式 API,这个功能对代码维护提供了很大帮助. 我们可以看下Vue2之前的代码结构,借用官方文档的图,说明如下: 再来看下使用组合式API 后的代码结构,借用网上的图例,说明 ...

  6. 【Vue3】vue3中组合式Api的setup写法快速入门上手起步

    要使用Vue3,那必须得会setup,因为setup是组合式API表演的舞台. 安装volar 如果你的VScode之前安装有vuter插件,请先把他禁用或者卸载掉,然后安装volar. 因为,vut ...

  7. 探秘 Vue3.0 - Composition API 在真实业务中的尝鲜姿势

    前言 2019年2月6号,React 发布 16.8.0 版本,新增 Hooks 特性.随即,Vue 在 2019 的各大 JSConf 中也宣告了 Vue3.0 最重要的 RFC,即 Functio ...

  8. Vue3.0 Composition API与Vue2.x 使用的 Options API

    Vue3.0 所采用的 Composition API 与 Vue2.x 使用的 Options API 有什么不同 开始之前 Composition API 可以说是Vue3最大的特点,那么为什么要 ...

  9. 某游戏公司基于OceanBase 4.0的运营分析AP实践

    本文转载自:OceanBase 社区 作者简介:黄友鹏,现就职于深圳某游戏公司,是一名Java开发,主要负责公司的业务调优和故障处理,熟悉MySQL,近年来研究分布式数据库. 导语:一家运营海外休闲游 ...

最新文章

  1. ODBC更新记录集提示”记录集为只读“
  2. 乱谈管理(优秀部门经理的职业素质)
  3. git stash pop_git常用命令行背诵并默写(全)
  4. 快速入门脑机接口:BCI基础(二)
  5. sublime老版本历史版本的linux包下载地址
  6. html下拉框选中第二个,html通过点击第一个select改变第二关select的内容
  7. 【JEEWX企业号专题】JEEWX与微信企业号对接,激活应用回调模式
  8. 机器视觉【基础】什么是机器视觉?
  9. 游戏软件性能测试怎么做?常规测试知识要点总结
  10. matlab中gama,matlab积分结果中的gamma()函数参数问题,急求解答!!!
  11. ITU-R BT.1788建议书 对多媒体应用中视频质量的主观评估方法
  12. C# HttpWebRequest 填充IP代理
  13. 下面以Button组件为例,开始FLEX皮肤制作的入门。
  14. iOS-PingFangSC字体
  15. 计算机键盘都起什么作用,电脑键盘各个按键功能 电脑键盘各个按键有什么功能...
  16. 关于Python中rank()函数的理解
  17. (银行案例)智能营销赋能大零售转型
  18. 微信小程序语音搜索功能
  19. android profile 打包_Android 利用 Managed Profile 确保兼容性
  20. 2017南开秋奥鹏作业计算机,南开17秋学期《DirectX程序设计》在线作业(资料)...

热门文章

  1. php 头bom_关于php中bom头的简介
  2. 苹果7plus元件分布图_苹果iphone7 plus手机拆解全过程评测 iphone7 plus拆机图解教程...
  3. Pycharm切换anaconda的环境
  4. 使用KubeKey 离线环境部署 KubeSphere v3.0.0
  5. 基于阿里云镜像源使用kubeadm安装k8s单master节点集群(v1.17.3)
  6. linux diff命令使用示例
  7. kafka后台启动命令
  8. Pyhon Django 表单类ModelForm注册案例(可直接操作数据库)
  9. spring aop记录日志方案设计
  10. centos中如何找出系统中 load 高时处于运行队列的进程