浅谈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 相关文章推荐
JS类中定义原型方法的两种实现的区别
Mar 08 Javascript
jQuery的实现原理的模拟代码 -4 重要的扩展函数 extend
Aug 03 Javascript
浅谈node.js中async异步编程
Oct 22 Javascript
JavaScript实现的MD5算法完整实例
Feb 02 Javascript
实践中学习AngularJS表单
Mar 21 Javascript
java中String类型变量的赋值问题介绍
Mar 23 Javascript
浅谈几种常用的JS类定义方法
Jun 08 Javascript
如何用JS/HTML将时间戳转换为“xx天前”的形式
Feb 06 Javascript
纯js实现html转pdf的简单实例(推荐)
Feb 16 Javascript
jQuery EasyUI Draggable拖动组件
Mar 01 Javascript
基于vue实现微博三方登录流程解析
Nov 04 Javascript
详解Java中String JSONObject JSONArray List转换
Nov 13 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
很温暖很温暖的Lester Young
2021/03/03 冲泡冲煮
php生成随机密码自定义函数代码(简单快速)
2014/05/10 PHP
PHP IDE phpstorm 常用快捷键
2015/05/18 PHP
简单谈谈PHP中的trait
2017/02/25 PHP
js 未结束的字符串常量错误解决方法
2010/06/13 Javascript
理解Javascript_07_理解instanceof实现原理
2010/10/15 Javascript
jquery打开直接跳到网页最下面、最低端实现代码
2013/04/22 Javascript
javascript列表框操作函数集合汇总
2013/11/28 Javascript
JavaScript中数据结构与算法(四):串(BF)
2015/06/19 Javascript
JavaScript+canvas实现七色板效果实例
2016/02/18 Javascript
VUE实现日历组件功能
2017/03/13 Javascript
Vue利用History记录上一页面的数据方法实例
2018/11/02 Javascript
vue-router传递参数的几种方式实例详解
2018/11/13 Javascript
微信小程序使用component自定义toast弹窗效果
2018/11/27 Javascript
小程序自定义单页面、全局导航栏的实现代码
2019/03/15 Javascript
原生js实现3D轮播图
2020/03/21 Javascript
解决vant的Toast组件时提示not defined的问题
2020/11/11 Javascript
Javascript实现单选框效果
2020/12/09 Javascript
[02:48]DOTA2英雄基础教程 拉席克
2013/12/12 DOTA
[53:10]2018DOTA2亚洲邀请赛 4.6 淘汰赛 VP vs VG 第一场
2018/04/11 DOTA
pyqt5与matplotlib的完美结合实例
2019/06/21 Python
python 单线程和异步协程工作方式解析
2019/09/28 Python
pygame实现贪吃蛇游戏(上)
2019/10/29 Python
python几种常用功能实现代码实例
2019/12/25 Python
Python selenium抓取虎牙短视频代码实例
2020/03/02 Python
Python能做什么
2020/06/02 Python
matplotlib更改窗口图标的方法示例
2021/02/03 Python
中国电子产品外贸网站:MiniIntheBox
2017/02/06 全球购物
来自Ocado的宠物商店:Fetch
2018/07/10 全球购物
SOA面试题:如何在SOA中实现松耦合
2013/07/21 面试题
体育教育专业自荐信范文
2013/12/20 职场文书
光信息科学与技术专业职业生涯规划
2014/03/13 职场文书
2014银行领导班子群众路线对照检查材料思想汇报
2014/09/17 职场文书
财务统计员岗位职责
2015/04/14 职场文书
oracle设置密码复杂度及设置超时退出的功能
2022/06/28 Oracle
CentOS7设置ssh服务以及端口修改方式
2022/12/24 Servers