Vue3CompositionAPI如何替换VueMixins
想在你的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() {
}
},
computed: {
double () {
unt * 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();
js合并两个数组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⽆疑是优越的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。