Vue3 Composition API如何替换Vue Mixins

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

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

回顾Mixins功能

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

一般,Vue组件是由一个JavaScript对象定义的,它具备表示咱们所需功能的各类属性——诸如 datamethodscomputed 等。vue

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

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

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

如今,咱们能够经过将其分配给 mixin config属性将其添加到任何使用的组件中。在运行时,Vue会将组件的属性与任何添加的mixin合并。npm

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

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

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

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模式如何在运行时合并两个对象。若是他们两个都共享同名属性,会发生什么?ide

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函数。注意,咱们须要更改子属性 countvalue 才能更改响应式变量。这是由于使用 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无疑是优越的。


原文:css-tricks.com/how-the-vue…

做者:Anthony Gore


文章首发于公众号 《前端外文精选》,私信回复:大礼包,送某网精品视频课程网盘资料,准能为你节省很多钱!

相关文章
相关标签/搜索