前段时间用 vue3 做了个项目,由于是第一次用 vue3,Composition API 的写法上和过去用过的 vue2 有诸多差异,在逻辑复用这块改善明显,简单和过去的 mixins(官方中文叫“混入”)比较一下。

vue2 mixins

在vue2中,我通常会使用mixin来复用代码,在多个组件里复用相同的逻辑,在组件中使用 mixin 时,mixin 对象中的所有选项都会混合到组件自己对应的选项里。当组件和 mixin 对象含有同名选项时,这些选项会合并。

例子:

// 定义一个mixin对象
var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

// 定义一个使用mixin对象的组件
new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})

vue2 的 mixins 有很多让人诟病的地方,mixins 的深度合并非常隐式,这让代码逻辑更难理解和调试,具体表现为如下几点:

  1. mixins 容易冲突:因为每个特性的属性都被合并到同一个组件中,组件内同名的属性或方法会把 mixins 里的覆盖掉。
  2. 可重用性有限:不能向 mixins 传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑方面的灵活性。
  3. 数据来源不清晰:组件里所使用的 mixins 里的数据或方法在当前组件代码里搜索不到,易造成错误的解读,比如被当成错误代码或冗余代码而误删。

vue3 自定义 hooks

vue3 的 Composition Api 中,有点像 react,可以用自定义 hooks 来实现逻辑复用,“以函数形式抽离一些可复用的方法像钩子一样挂着,随时可以引入和调用,实现高内聚低耦合的目标”,总结一下就是:

  1. 将可复用功能抽离为外部 js 文件
  2. 函数名/文件名以 use 开头,形如:useXXX
  3. 引用时将响应式变量或者方法显式解构暴露出来如:const {nameRef,Fn} = useXX()

一个计数器的例子:

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

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

// App.vue
<template>
  <p>{{ count }}</p>
  <p>{{ double }}</p>
  <button @click="increment">increment</button>
</template>

<script>
import useCounter from './useCounter'

export default {
  setup() {
    const { count, double, increment} = useCounter()
    return {
      count,
      double,
      increment
    }
  },
}
</script>

可以看到,这里没有再使用mixins属性来引入逻辑代码,本质上只是封装了一个 js 文件引入使用而已,这样使用后,数据来源清晰可见,代码引用有依有据。mixin 中的命名冲突及维护不易的问题也可以被解决。

所以当组件间需要共用逻辑时,建议使用 vue3 中的 Composition API,完全避免了 mixin 方式的种种痛点。

参考资料

https://cn.vuejs.org/v2/guide/mixins.html
https://www.jianshu.com/p/6e519d661b6f