写点什么

Vue3 Composition API 如何替换 Vue Mixins

作者:源字节1号
  • 2022 年 6 月 13 日
  • 本文字数:3236 字

    阅读完需:约 11 分钟

Vue3 Composition API如何替换Vue Mixins

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

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

回顾 Mixins 功能

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

// MyComponent.jsexport default { data: () => ({  myDataProperty: null }), methods: {  myMethod () { ... } } // ...}
复制代码

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

// MyMixin.jsexport default { data: () => ({  mySharedDataProperty: null }), methods: {  mySharedMethod () { ... } }}
复制代码

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

// ConsumingComponent.jsimport 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.vueexport default { data: () => ({  count: 0 }), methods: {  increment() {   this.count++;  } }, computed: {  double () {   return this.count * 2;  } }}
复制代码

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

// Counter.vueimport { 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 函数中将其返回。

代码提取

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

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

//useCounter.jsimport { 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.jsimport 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 无疑是优越的。


如若转载,请注明出处:开源字节   https://sourcebyte.cn/article/159.html

用户头像

源字节1号

关注

一个着迷于技术又喜欢不断折腾的技术活跃者 2022.03.09 加入

一个着迷于技术又喜欢不断折腾的技术活跃者。喜欢并热爱编程,执着于努力之后所带来的美好生活!

评论

发布
暂无评论
Vue3 Composition API如何替换Vue Mixins_前端开发_源字节1号_InfoQ写作社区