浅谈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 相关文章推荐
HTML页面如何象ASP一样接受参数
Feb 07 Javascript
jquery选择器需要注意的问题
Nov 26 Javascript
javascript跨域总结之window.name实现的跨域数据传输
Nov 01 Javascript
JavaScript字符串删除重复字符的方法
Dec 25 Javascript
基于JavaScript实现跳转提示页面
Sep 24 Javascript
jQuery插件HighCharts绘制2D柱状图、折线图和饼图的组合图效果示例【附demo源码下载】
Mar 09 Javascript
vue下拉列表功能实例代码
Apr 08 Javascript
详解javascript 正则表达式之分组与前瞻匹配
May 30 Javascript
在vue中使用vue-echarts-v3的实例代码
Sep 13 Javascript
javascript设计模式 ? 访问者模式原理与用法实例分析
Apr 26 Javascript
vue项目中使用rem,在入口文件添加内容操作
Nov 11 Javascript
如何利用React实现图片识别App
Feb 18 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
main.php
2006/12/09 PHP
php实现改变图片直接打开为下载的方法
2015/04/14 PHP
php禁用cookie后session设置方法分析
2016/10/19 PHP
javascript实现的listview效果
2007/04/28 Javascript
javascript HTMLEncode HTMLDecode的完整实例(兼容ie和火狐)
2009/06/02 Javascript
jQuery LigerUI 插件介绍及使用之ligerDrag和ligerResizable示例代码打包
2011/04/06 Javascript
JQuery之focus函数使用介绍
2013/08/20 Javascript
Javascript表格翻页效果的具体实现
2013/10/05 Javascript
JS的encodeURI和java的URLDecoder.decode使用介绍
2014/05/08 Javascript
在NodeJS中启用ECMAScript 6小结(windos以及Linux)
2014/07/15 NodeJs
jquery和css3实现的炫酷时尚的菜单导航
2014/09/01 Javascript
60行js代码实现俄罗斯方块
2015/03/31 Javascript
JavaScript多并发问题如何处理
2015/10/28 Javascript
JavaScript html5 canvas绘制时钟效果(二)
2016/03/27 Javascript
50 个 jQuery 插件可将你的网站带到另外一个高度
2016/04/26 Javascript
第六篇Bootstrap表格样式介绍
2016/06/21 Javascript
inner join 内联与left join 左联的实例代码
2017/09/18 Javascript
vue实现仿淘宝结账页面实例代码
2017/11/08 Javascript
你可能不知道的CORS跨域资源共享
2019/03/13 Javascript
微信小程序实现卡片左右滑动效果的示例代码
2019/05/01 Javascript
微信小程序实现商城倒计时
2020/11/01 Javascript
JS实现密码框效果
2020/09/10 Javascript
如何封装Vue Element的table表格组件
2021/02/06 Vue.js
Python中使用asyncio 封装文件读写
2016/09/11 Python
非递归的输出1-N的全排列实例(推荐)
2017/04/11 Python
python多个模块py文件的数据共享实例
2019/01/11 Python
python实现简单猜单词游戏
2020/12/24 Python
html5 video全屏播放/自动播放的实现示例
2020/08/06 HTML / CSS
伯克斯奥特莱斯:Burkes Outlet
2019/03/30 全球购物
局部内部类是否可以访问非final变量?
2013/04/20 面试题
JDK安装目录下有哪些内容
2014/08/25 面试题
校长竞聘演讲稿
2014/05/16 职场文书
2015年感恩节活动总结
2015/03/24 职场文书
CSS3实现模糊背景的三种效果示例
2021/03/30 HTML / CSS
详解nginx进程锁的实现
2021/06/14 Servers
Python socket如何解析HTTP请求内容
2022/02/12 Python