浅谈Vue3 Composition API如何替换Vue Mixins


Posted in Javascript onApril 29, 2020

想在你的Vue组件之间共享代码?如果你熟悉Vue 2 则可能知道使用mixin,但是新的Composition API 提供了更好的解决方案。

在本文中,我们将研究mixins的缺点,并了解Composition API如何克服它们,并使Vue应用程序具有更大的可伸缩性。

回顾Mixins功能

让我们快速回顾一下mixins模式,因为对于下一部分我们将要讲到的内容,请务必将其放在首位。

通常,Vue组件是由一个JavaScript对象定义的,它具有表示我们所需功能的各种属性——诸如 data,methods,computed 等。

// MyComponent.js
export default {
 data: () => ({
  myDataProperty: null
 }),
 methods: {
  myMethod () { ... }
 }
 // ...
}

当我们想在组件之间共享相同的属性时,可以将公共属性提取到一个单独的模块中:

// MyMixin.js
export default {
 data: () => ({
  mySharedDataProperty: null
 }),
 methods: {
  mySharedMethod () { ... }
 }
}

现在,我们可以通过将其分配给 mixin config属性将其添加到任何使用的组件中。在运行时,Vue会将组件的属性与任何添加的mixin合并。

// ConsumingComponent.js
import MyMixin from "./MyMixin.js";

export default {
 mixins: [MyMixin],
 data: () => ({
  myLocalDataProperty: null
 }),
 methods: {
  myLocalMethod () { ... }
 }
}

对于这个特定的例子,在运行时使用的组件定义应该是这样的:

export default {
 data: () => ({
  mySharedDataProperty: null
  myLocalDataProperty: null
 }),
 methods: {
  mySharedMethod () { ... },
  myLocalMethod () { ... }
 }
}

Mixins被认为“有害”

早在2016年中期,丹·阿布拉莫夫(Dan Abramov)就写了《mixin被认为是有害的》(mixin Considered Harmful),他在书中辩称,将mixin用于在React组件中重用逻辑是一种反模式,主张远离它们。

不幸的是,他提到的关于React mixins的缺点同样适用于Vue。在了解Composition API如何克服这些缺点之前,让我们熟悉这些缺点。

命名冲突

我们看到了mixin模式如何在运行时合并两个对象。如果他们两个都共享同名属性,会发生什么?

const mixin = {
 data: () => ({
  myProp: null
 })
}

export default {
 mixins: [mixin],
 data: () => ({
  // 同名!
  myProp: null
 })
}

这就是合并策略发挥作用的地方。这是一组规则,用于确定当一个组件包含多个具有相同名称的选项时会发生什么。

Vue组件的默认(但可以配置)合并策略指示本地选项将覆盖mixin选项。Vue组件的默认(可选配置)合并策略指示本地选项将覆盖mixin选项。不过也有例外,例如,如果我们有多个相同类型的生命周期钩子,这些钩子将被添加到一个钩子数组中,并且所有的钩子都将被依次调用。

尽管我们不应该遇到任何实际的错误,但是在跨多个组件和mixin处理命名属性时,编写代码变得越来越困难。一旦第三方mixin作为带有自己命名属性的npm包被添加进来,就会特别困难,因为它们可能会导致冲突。

隐式依赖

mixin和使用它的组件之间没有层次关系。这意味着组件可以使用mixin中定义的数据属性(例如mySharedDataProperty),但是mixin也可以使用假定在组件中定义的数据属性(例如myLocalDataProperty)。这种情况通常是在mixin被用于共享输入验证时出现的,mixin可能会期望一个组件有一个输入值,它将在自己的validate方法中使用。

不过,这可能会引起一些问题。如果我们以后想重构一个组件,改变了mixin需要的变量的名称,会发生什么情况呢?我们在看这个组件时,不会发现有什么问题。linter也不会发现它,我们只会在运行时看到错误。

现在想象一个有很多mixin的组件。我们可以重构本地数据属性吗?或者它会破坏mixin吗?我们得手动搜索才能知道。

从mixins迁移

mixin的替代方案,包括高阶组件,utility 方法和其他一些组件组成模式。

mixins的缺点是Composition API背后的主要推动因素之一,让我们快速了解一下它是如何工作的,然后再看它如何克服mixin问题。

快速入门Composition API

Composition API的主要思想是,我们将它们定义为从新的 setup 函数返回的JavaScript变量,而不是将组件的功能(例如state、method、computed等)定义为对象属性。

以这个经典的Vue 2组件为例,它定义了一个“计数器”功能:

//Counter.vue
export default {
 data: () => ({
  count: 0
 }),
 methods: {
  increment() {
   this.count++;
  }
 },
 computed: {
  double () {
   return this.count * 2;
  }
 }
}

下面是使用Composition API定义的完全相同的组件。

// Counter.vue
import { ref, computed } from "vue";

export default {
 setup() {
  const count = ref(0);
  const double = computed(() => count * 2)
  function increment() {
   count.value++;
  }
  return {
   count,
   double,
   increment
  }
 }
}

首先会注意到,我们导入了 ref 函数,该函数允许我们定义一个响应式变量,其作用与 data 变量几乎相同。计算属性的情况与此相同。

increment 方法不是被动的,所以它可以被声明为一个普通的JavaScript函数。注意,我们需要更改子属性 count 的 value 才能更改响应式变量。这是因为使用 ref 创建的响应式变量必须是对象,以便在传递时保持其响应式。

定义完这些功能后,我们将从 setup 函数中将其返回。上面两个组件之间的功能没有区别,我们所做的只是使用替代API。

代码提取

Composition API的第一个明显优点是提取逻辑很容易。

让我们使用Composition API重构上面定义的组件,以使我们定义的功能位于JavaScript模块 useCounter 中(在特性描述前面加上“use”是一种Composition API命名约定。)。

//useCounter.js
import { ref, computed } from "vue";

export default function () {
 const count = ref(0);
 const double = computed(() => count * 2)
 function increment() {
  count.value++;
 }
 return {
  count,
  double,
  increment
 }
}

代码重用

要在组件中使用该函数,我们只需将模块导入组件文件并调用它(注意导入是一个函数)。这将返回我们定义的变量,随后我们可以从 setup 函数中返回它们。

// MyComponent.js
import useCounter from "./useCounter.js";

export default {
 setup() {
  const { count, double, increment } = useCounter();
  return {
   count,
   double,
   increment
  }
 }
}

乍一看,这似乎有点冗长而毫无意义,但让我们来看看这种模式如何克服了前面讨论的mixins问题。

命名冲突解决了
我们之前已经了解了mixin如何使用与消费者组件中的名称相同的属性,或者甚至更隐蔽地使用了消费者组件使用的其他mixin中的属性。

这不是Composition API的问题,因为我们需要显式命名任何状态或从合成函数返回的方法。

export default {
 setup () {
  const { someVar1, someMethod1 } = useCompFunction1();
  const { someVar2, someMethod2 } = useCompFunction2();
  return {
   someVar1,
   someMethod1,
   someVar2,
   someMethod2
  }
 }
}

命名冲突的解决方式与其他任何JavaScript变量相同。

隐式依赖…解决了!

前面还看到mixin如何使用在消费组件上定义的 data 属性,这可能会使代码变得脆弱,并且很难进行推理。

合成函数(Composition Function)还可以调用消费组件中定义的局部变量。不过,不同之处在于,现在必须将此变量显式传递给合成函数。

import useCompFunction from "./useCompFunction";

export default {
 setup () {
  // 某个局部值的合成函数需要用到
  const myLocalVal = ref(0);

  // 它必须作为参数显式地传递
  const { ... } = useCompFunction(myLocalVal);
 }
}

总结

mixin模式表面上看起来很安全。然而,通过合并对象来共享代码,由于它给代码增加了脆弱性,并且掩盖了推理功能的能力,因此成为一种反模式。

Composition API最聪明的部分是,它允许Vue依靠原生JavaScript中内置的保障措施来共享代码,比如将变量传递给函数和模块系统。

这是否意味着Composition API在各方面都比Vue的经典API优越?不是的。在大多数情况下,你坚持使用经典API是没有问题的。但是,如果你打算重用代码,Composition API无疑是优越的。

到此这篇关于浅谈Vue3 Composition API如何替换Vue Mixins的文章就介绍到这了,更多相关Vue3 Composition 替换Vue Mixins内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木! 

Javascript 相关文章推荐
如何用javascript控制上传文件的大小
Oct 26 Javascript
jQuery 1.5 源码解读 面向中高阶JSER
Apr 05 Javascript
javascript中typeof的使用示例
Dec 19 Javascript
离开当前页面前使用js判断条件提示是否要离开页面
May 02 Javascript
js实现点击后将文字或图片复制到剪贴板的方法
Aug 04 Javascript
JS显示日历和天气的方法
Mar 01 Javascript
jQuery中$.each()函数的用法引申实例
May 12 Javascript
使用jQuery中的wrap()函数操作HTML元素的教程
May 24 Javascript
vue.js获取数据库数据实例代码
May 26 Javascript
利用Vue2.x开发实现JSON树的方法
Jan 04 Javascript
vue-router中的hash和history两种模式的区别
Jul 17 Javascript
vue悬浮可拖拽悬浮按钮的实例代码
Aug 20 Javascript
JS数组Reduce方法功能与用法实例详解
Apr 29 #Javascript
JavaScript实现放大镜效果代码示例
Apr 29 #Javascript
React组件设计模式之组合组件应用实例分析
Apr 29 #Javascript
Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解
Apr 29 #Javascript
JS数组push、unshift、pop、shift方法的实现与使用方法示例
Apr 29 #Javascript
JS实现手写 forEach算法示例
Apr 29 #Javascript
Node.js API详解之 querystring用法实例分析
Apr 29 #Javascript
You might like
PHP iconv 解决utf-8和gb2312编码转换问题
2010/04/12 PHP
为你总结一些php信息函数
2015/10/21 PHP
php正则去除网页中所有的html,js,css,注释的实现方法
2016/11/03 PHP
js压缩利器
2007/02/20 Javascript
Jquery作者John Resig自己封装的javascript 常用函数
2009/11/09 Javascript
javascript 跳转代码集合
2009/12/03 Javascript
JS打开层/关闭层/移动层动画效果的实例代码
2013/05/11 Javascript
使用iframe window的scroll方法控制iframe页面滚动
2014/03/05 Javascript
原生js实现fadein 和 fadeout淡入淡出效果
2014/06/05 Javascript
javascript实现浏览器窗口传递参数的方法
2014/09/03 Javascript
jQuery操作DOM之获取表单控件的值
2015/01/23 Javascript
JavaScript中实现单体模式分享
2015/01/29 Javascript
Angular中$compile源码分析
2016/01/28 Javascript
JS+html5 canvas实现的简单绘制折线图效果示例
2017/03/13 Javascript
用Node提供静态文件服务的方法
2018/07/06 Javascript
vue cli3.0 引入eslint 结合vscode使用
2019/05/27 Javascript
JS根据json数组多个字段排序及json数组常用操作
2019/06/06 Javascript
vue限制输入框只能输入8位整数和2位小数的代码
2019/11/06 Javascript
el-table表头根据内容自适应完美解决表头错位和固定列错位
2021/01/07 Javascript
Python中type的构造函数参数含义说明
2015/06/21 Python
使用pyecharts生成Echarts网页的实例
2019/08/12 Python
python根据时间获取周数代码实例
2019/09/30 Python
python3 求约数的实例
2019/12/05 Python
基于Python fminunc 的替代方法
2020/02/29 Python
利用 Python ElementTree 生成 xml的实例
2020/03/06 Python
python实现简单遗传算法
2020/09/18 Python
阿根廷首家户外用品制造商和经销商:Montagne
2018/02/12 全球购物
巴西儿童时尚购物网站:Dinda
2019/08/14 全球购物
某个公司的Java笔面试题
2016/03/11 面试题
消防战士优秀事迹材料
2014/02/13 职场文书
2014年廉洁自律承诺书
2014/05/26 职场文书
python plt.plot bar 如何设置绘图尺寸大小
2021/06/01 Python
go使用Gin框架利用阿里云实现短信验证码功能
2021/08/04 Golang
JVM之方法返回地址详解
2022/02/28 Java/Android
Java8 Stream API 提供了一种高效且易于使用的处理数据的方式
2022/04/13 Java/Android
vue组件vue-esign实现电子签名
2022/04/21 Vue.js