Vue中Mixin的正确用法详解
作者:cxy930123
令人头疼的 mixin
众所周知,vue 的 mixins
是个非常灵活,但又很容易带来混乱的 API。不知道你是否像我一样,常常因为项目中用了 mixins
而感到头疼。比如说:
- 来回切换文件:有人会因为文件太大而把一部分内容提取到单独的
mixin
中,看起来文件变小了,但改代码的时候需要在多个文件之间不停切换 - 找不到函数和变量:模板中用到一个函数,在
methods
中搜索不到,然后发现组件引入了很多mixins
,不知道要该去哪里找
Mixins
本该是一种强大的重用代码的手段,但使用之后往往带来更多的混乱,代码变得不易维护。所以,在 Vue3 中,不再推荐使用 mixins
,而是改用组合式 API的方式来重用代码。
那么,我们是不是不应该在日常代码中使用 mixins
呢?我觉得也不是。Mixins
是个强大的工具,但本身存在一些严重的问题,如果我们能把 mixins
中存在的问题解决掉,那剩下的就会是:强大。
为什么会这样?
面对 mixins
这样灵活的工具,我们必须理解它的问题所在,才能避免在使用时带来混乱。
本质上,mixins
是一种重用代码的手段,它可以向一个对象中注入另一个对象中的所有属性。并且,一个对象可以同时从多个不同对象中注入属性,所以非常灵活。而这样的灵活导致了一些致命的问题:
- 来源不明:使用的
mixins
越多,越难找到某个属性或方法来自哪里 - 无法精确引入:引入一个
mixin
时,会引入这个mixin
的全部属性,因此很可能会引入一些用不到的东西,也会导致很难清楚知道到底引入了哪些属性和方法 - 命名冲突:引入多个
mixins
时,引入的属性名可能会冲突 - 耦合问题:不规范地使用
mixins
功能,可能会导致耦合问题,比如因为组件太大而抽离一部分属性到mixins
中,这样产生的mixins
一般会互相依赖,造成强耦合
作为对比,我们看看其他类似技术为什么没有这些问题:
- ES模块:一个模块可以导出很多东西,但我们可以选择性导入,这样就能清楚知道导入了哪些内容,并且导入的变量和函数可以重命名,这样就解决了命名冲突的问题
- Vue3组合式函数:Vue3 推荐使用组合式函数来替代
mixins
实现逻辑重用,组合式函数一般返回一个对象,解构时可以只取出对象中的部分属性,从而可以精确控制引入内容,并且解构语法支持自定义变量名,因此不会导致命名冲突
如果我们想要放心使用 mixins
功能,就必须解决以上问题。从 ES模块 和 Vue3组合式函数 的设计,我们可以看出一些关键点,那就是需要能 精确控制引入的内容,具体包括:
- 清晰看到引入的内容:在引入
mixins
的地方,可以直接看到引入了哪些属性和方法 - 支持部分引入:可以只引入需要用到的属性,而不是引入
mixin
对象上的全部属性 - 自定义引入的属性名:可以指定要引入的属性和方法的名字,避免命名冲突
这看起来似乎是不可能的,因为 mixin
就是一个对象,一旦引入就要接受这个对象上的所有属性和方法,也不可能在引入的时候去修改对象的属性名。
那还能不能用?
当然,你可以只使用生命周期钩子的功能,而不引入 data
、计算属性和方法,这样就不会往实例上添加额外属性,也就不会有这些问题。但是,这会使 mixins
的功能大打折扣。
幸运的是,确实有一个方法能够精确控制引入的内容,那就是动态创建 mixin
对象。
一般来说,我们习惯把 mixin
定义为一个对象,然后在组件中直接引入。但其实只要把它定义成一个函数,问题就会得到解决。
我们可以把 mixin
定义成一个函数,通过函数参数接收需要引入的属性名,然后生成并返回真正的 mixin
对象。这样的 mixin
就是动态生成的。引入时,调用这个函数,通过传入的参数就可以精确控制引入的内容,可以指定要引入哪些属性,也可以指定引入的属性名字。
import mouse from '@/mixins/mouse' export default { mixins: [ mouse({ mouseX: 'x', mouseY: 'y' }) ] }
我们给这种方法取一个不冲突的名字,就称为 动态Mixin 好了。
当然,这种写法增加了编写 mixins
的难度,所以我认为,mixins
适合用来引入一些简单的属性。比如,VueUse 中的那些工具方法就很适合使用动态 Mixin 来实现。
动态Mixin示例
接下来我列举一些自己在项目中经常使用的 mixins
,大家可以体会一下这种动态 Mixin 的适用场景以及带来的好处。
倒计时
倒计时是业务中经常会用到的一种场景,比如发送验证码时一般会显示倒计时。然后有些使用须知的弹窗,确认按钮也会在倒计时结束后才允许点击。
这种倒计时非常简单,只需要一个记录当前剩余时间的属性,加上一个设置倒计时时长的函数。这种场景就非常适合使用 mixins
来实现。实现过程先不考虑,使用效果是这样的:
<template> <button :disabled="!!seconds" @click="setCountdown(60)"> {{ seconds || '发送验证码' }} </button> </template> <script> import countdown from '@/mixins/countdown'; export default { // 引入 seconds 属性存储剩余秒数 // 引入 setCountdown 方法设置倒计时秒数 mixins: [countdown('seconds', 'setCountdown')], }; </script>
这个 mixins
引入了一个属性和一个方法,引入的内容都是可以指定名字的。调用 setCountdown
方法时会设置 seconds
属性的值,并重新开始倒计时。
不过在 Vue 中,我们可以充分利用 Vue 自带的响应式特性。我们可以利用 watch
去进行监听,在检测到 seconds
变化时,重新开始倒计时。这样就可以少引入一个方法。
<template> <button :disabled="!!seconds" @click="seconds = 60"> {{ seconds || '发送验证码' }} </button> </template> <script> import countdown from '@/mixins/countdown'; export default { // 引入 seconds 属性记录倒计时秒数 mixins: [countdown('seconds')], }; </script>
看,使用 mixins
之后,代码变得特别简洁清晰。而且只需要写一次,今后就可以在其他项目中直接使用这个 mixin
。而如果不使用 mixins
自己实现,就需要多写很多代码,完善的写法每次还需要考虑怎么清除定时器,防止内存泄漏。
这个 mixin
的具体实现可以看这里:vue-library/mixins/countdown at master · web1706/vue-library · GitHub。你可以直接在你的项目中使用。
路由query参数
编写一个操作路由 query 参数的 mixin
其实是挺有必要的。
想象这样一个场景:页面上有一个 Tabs
组件,有多个页签。当我们切换页签时,一般会把当前激活的页签的值存储在 data
中。
export default { data() { return { // 当前 tab 页 currentTab: 'one' } } }
这样其实会有一个问题,就是刷新页面后,或者进入其他页面再返回时,是回不到之前的页签的。如果有一天需要改成刷新时保留激活的页签,最好的方法就是把页签值存入路由 query 中。这时如果有这样一个操作 query 参数的 mixin
就方便了。
import routeQuery from '@/mixins/route-query'; export default { mixins: [ // 当前 tab 页 routeQuery('currentTab'), ], };
这个 mixin
会从 this.$route.query
中读取对应的参数值,放到名为 currentTab
的属性中。并且这个属性是可写的!写入时会自动调用 this.$router.replace
去替换 query 中的参数值。
当然,你可能还需要指定默认值,这也没问题,只需要改成对象形式:
import routeQuery from '@/mixins/route-query'; export default { mixins: [ routeQuery({ // 当前 tab 页 currentTab: { default: 'one' } }), ], };
或许,你想在 query 中使用一个不一样的参数名。
import routeQuery from '@/mixins/route-query'; export default { mixins: [ routeQuery({ // 当前 tab 页 currentTab: { from: 'tab' }, // 简写形式 activeTab: 'tab' }), ], };
想要使用数字类型也没问题。
export default { mixins: [ routeQuery({ // 对引入的属性值进行转换 companyId: { type: Number, }, // 简写形式 deptId: Number, }), ], };
可以看到,这个 mixin
非常灵活,功能也比传统 mixins
强大很多。
后记
本文介绍的这种使用函数动态生成 Mixin 的方式,大家觉得怎么样呢?大家平时又是如何看待和使用 Mixin 的呢?快来交流一下各自的想法吧。
以上就是Vue中Mixin的正确用法详解的详细内容,更多关于Vue Mixin用法的资料请关注脚本之家其它相关文章!