目录
前言
什么是 mixins?
如何创建 mixins?
如何使用 mixins
mixins 的特点
方法和参数在各组件中不共享
mixins 与组件冲突
冲突之 合并+覆盖
冲突之 合并
全局 mixins
mixins 中有异步请求的情况
与 vuex 的区别
与公共组件的区别
前言
在项目开发的时候,常会碰到这样的一种现象:有两个组件非常相似,比如较为熟悉的 Modal 、 Tooltip 和 Popover ,它们都具有同样的基本函数,而且它们之间也有足够的不同。很多时候,就让人很难做出选择:是把它们拆会成多个不同的组件呢?还是只使用一个组件,创建足够的属性来改变不同的情况?
这些解决方案都不够完美。如果拆分成多个组件,就不得不冒着如果功能变动你要在多个文件中更新它的风险。另一方面,太多的属性会很快变得混乱,难维护,甚至对于组件开发者自已面言,也是件难事。
在Vue中,对于这样的场景,官方提供了一种叫混入 (mixins) 的特性。可以利用 mixins 将公共部分提取出来,使用 mixins 允许你封装一块在应用的其他组件中都可以使用的函数。如果被正确的使用,他们不会改变函数作用域外部的任何东西,所以多次执行,只要是同样的输入,总是能得到一样的值。
什么是 mixins?
混入 (mixins) 是一个 JavaScript 对象,可以包含任意组件选项,比如 Vue 实例中生命周期的各个钩子函数,也可以是 data 、components 、methods 或 directives 等。当组件使用混入对象时,所有混入对象的选项将被混合进入该组件本身的选项。
如何创建 mixins?
在 src 目录下创建一个 mixins 文件夹,在文件夹下新建一个 myMixins.js 文件。因为 mixins 是一个 js 对象,所以应该以对象的形式来定义 myMixins,在对象中可以和 vue 组件一样来定义 data、components、methods、created、computed 等属性,并通过 export 导出该对象。
如何使用 mixins
在需要调用的组件中引入 myMixins.js 文件,然后在 export default 中引入需要的对象即可。
<script>
import { myMixins } from "@/mixins/myMixins.js"
export default {mixins: [myMixins]
}
</script>
mixins 的特点
方法和参数在各组件中不共享
虽然组件调用了 mixins 并将其属性合并到自身组件中来了,但是其属性只会被当前组件所识别并不会共享,也就是其他组件无法从当前组件中获取到 mixins 中的数据和方法。
① 首先在 myMixins.js 中定义一个 age 字段和 getAge 方法
export const myMixins = {components:{},data() {return {age: 18,}},mounted() {this.getAge()},methods: {getAge() {console.log(this.age)}}
}
② 组件1中对 num 进行 +1 操作
import { myMixins } from "@/mixins/myMixins.js";
export default {mixins: [myMixins],data() {return {}},created() {this.age++},
}
③ 组件2不进行操作
export default {mixins: [myMixins],data() {return {}},
}
④ 我们分别切换到两个页面,查看控制台输出。
会发现组件1改变了 age 里面的值,组件2中 age 值还是混合对象的初始值,并没有随着组件1的增加而改变
mixins 与组件冲突
冲突之 合并+覆盖
- 数据对象(data)在内部会进行递归合并
- 值为对象(components、methods 、computed、directives)的选项将被合并为同一个对象。
- 键冲突时优先组件即组件中的键会覆盖混入对象。
① 在混入对象增加 age 属性、getAge1 方法和 getAge2 方法
// myMixins.js
export const myMixins = {components:{},data() {return {age: 18,}},methods: {getAge1() {console.log("age1 from mixins =", this.age )},getAge2() {console.log("age2 from mixins =", this.age )},}
}
② 我们在引入了 myMixins 文件的组件中,增加 age 属性、getAge1 方法和 getAge3 方法
// template.vue
import { myMixins } from "@/mixins/myMixins.js";
export default {mixins: [myMixins],data() {return {age: 20,}},mounted() {this.getAge1();this.getAge2();this.getAge3();},methods: {getAge1() {console.log('age1 from template =', this.age)},getAge3() {console.log('age3 from template =', this.age)},}
}
③ 我们会发现,组件中的 age 覆盖了混合对象的 age,组件的 getAge1 方法覆盖了混合对象的 getAge1 方法
冲突之 合并
watch选项、钩子函数混入组件时,选项会被合并到一个数组中,因此都会被执行。按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。
// myMixins.js
export const myMixins = {components:{},data() {return {}},created() {console.log('xxx from mixins')}
}
import { myMixins } from "@/mixins/myMixins.js";
export default {mixins: [myMixins],data() {return {}},created() {console.log('xxx from template')}
}
全局 mixins
当我们使用全局混合时,我们不是指能够在每个组件上访问它们,就像是过滤器一样。我们能够通过 mixins:[myMixins] 访问组件上的混合对象。
全局混合被注册到了每个单一组件上。因此,它们的使用场景极其有限并且要非常的小心。一个我能想到的用途就是它像一个插件,你需要赋予它访问所有东西的权限。但即使在这种情况下,我也对你正在做的保持警惕,尤其是你在应用中扩展的函数,可能对你来说是不可知的
为了创建一个全局实例,我们可以把它放在 Vue 实例之上。在一个典型的 Vue-cli 初始化的项目中,它可能在你的 main.js 文件中。
Vue.mixin({mounted() {console.log('hello from mixin!')}
})new Vue({...
})
谨慎使用全局混入对象,因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)。大多数情况下,只应当应用于自定义选项,就像上面示例一样。也可以将其用作 插件 以避免产生重复应用。
mixins 中有异步请求的情况
问题描述: 当混合里面包含异步请求函数,而我们又需要在组件中使用异步请求函数的返回值时,我们会取不到此返回值。如下:
// myMixins.js
export const myMixins = {components:{},data() {return {num: 1,}},methods: {getDate1() {new Promise((resolve,reject) => {let a = 1;setTimeout(() => {resolve(a)},500)}).then(res => {console.log(res,'res');return res})},}
}
// todo.vue
import { myMixins } from "./myMixins.js";export default {mixins: [myMixins],data() {return {}},mounted() {console.log(this.getDate1,'template1-func_one')}
}
打印结果:undefined "template1-func_one"
解决方案:不要返回结果,而是直接返回异步函数
// myMixins.js
export const myMixins = {components:{},data() {return {num: 1,}},methods: {async getDate1() {let result = await new Promise((resolve,reject) => {let a = 1;setTimeout(() => {resolve(1)},500)})return result},}
}
// todo.vue
import { myMixins } from "./myMixins.js";export default {mixins: [myMixins],data() {return {}},mounted() {this.getDate1().then(res => {console.log(res,'template1-res')})}
}
打印结果:1 "template1-res"
与 vuex 的区别
- vuex: 用来做状态管理的,里面定义的变量在每个组件中均可以使用和修改,在任一组件中修改此变量的值之后,其他组件中此变量的值也会随之修改。
- Mixins: 可以定义共用的变量,在每个组件中使用,引用组件中之后,各个变量是相互独立的,值的修改在组件中不会相互影响。
与公共组件的区别
- 组件: 在父组件中引入组件,相当于在父组件中给出一片独立的空间供子组件使用,然后根据 prop 来传值,但本质上两者是相对独立的。
- Mixins: 在引入组件之后与组件中的对象和方法进行合并,相当于扩展了父组件的对象与方法,可以理解为形成了一个新的组件。