前言
当我们的Vue项⽬越来越⼤,功能越来越多时,我们会发现组件之间可能存在很多相似的功能,你在⼀遍⼜⼀遍的复制粘贴相同的代码段(data,method,watch、mounted等)
,如果我们在每个组件中去重复定义这些属性和⽅法会使得项⽬出现代码冗余并提⾼了维护难度。
当然,我们可以将所有这些单独的文件编写为一个单独的组件,并使用 prop
来尝试自定义它们,但是使用这么多 props
很容易造成混乱且难懂。为了避免这个问题,大多数人只是继续添加重复的代码,尽管自己感觉应该有更好的解决方案。
针对这种情况官⽅提供了Mixins
特性。
什么是Mixins?
Mixins
(混入),官⽅的描述是⼀种分发 Vue 组件中可复⽤功能的⾮常灵活的⽅式,Mixins
是⼀个js对象。它可以包含我们使用任何组件选项如 data
、 components
、mounted
、created
、methods
、update
、 computed
等,我们只要将共⽤的功能以对象的⽅式传⼊ Mixins
选项中,当组件使⽤ Mixins
对象时所有 Mixins
对象的选项都将被混⼊该组件本⾝的选项中来,组件将有权访问Mixins
中的所有选项,就像在组件本身中声明的那样。
这样就可以提⾼代码的重⽤性,使你的代码保持⼲净和易于维护。
接着,我们通过示例来帮助加深一下映像:
// Mixins.js file
export default {
data () {
return{
msg: "Hello World"
}
},
created: function () {
console.log("这里由 Mixins 中 create 方法打印!")
},
methods: {
displayMessage: function () {
console.log("这里由 Mixins 方法里打印!")
}
}
}
// home.vue file
import Mixins from "./Mixins.js"
new Vue({
mixins: [Mixins],
created: function () {
console.log("这里由 home 中 create 方法打印!")
console.log(this.$data)
this.displayMessage()
}
})
// => "这里由 Mixins 中 create 方法打印!"
// => "这里由 这里由 home 中 create 方法打印!"
// => {msg: ‘Hello World’}
// => "这里由 Mixins 方法里打印!"
正如我们所看到的,在使用Mixins
之后,该组件包含Mixins
中的所有数据,并且可以通过使用this
来访问Mixins
中的数据和方法。我们还可以使用变量而不是单独的文件来定义Mixins
。
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
什么时候使用Mixins?
当我们存在多个组件中的数据或者功能很相近时,我们就可以利⽤ Mixins
将公共部分提取出来,通过 Mixins
封装的函数,组件调⽤他们是不会改变函数作⽤域外部的。
如果发生命名冲突该怎么办?
当Mixins
中的数据、方法或任何组件选项与组件中的选项具有相同的名称时,可能会发生组件与其Mixins
之间的命名冲突。数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。例如,如果在组件和Mixins
中都有一个title
数据变量。title
将返回组件中定义的值,如下所示:
// Mixins.js file
export default {
data () {
return{
title: "Mixin",
numberArr:[1,2,3,4,5,99,98],
objArr:[{id:'xxx',name:'xxx'},{id:'yyy',name:'yyy'},{id:3,name:'zzzz'}],
user: {
name: 'Jack',
id: "minix1",
address: {
prov: "minix2",
city: "minix3",
},
},
}
}
}
// home.vue file
import Mixins from "./Mixins.js"
export default {
mixins: [Mixins],
data(){
return{
title: "Component",
numberArr:[1,2,3,4,5],
objArr:[{id:1,name:1},{id:2,name:2},{id:3,name:3}],
user: {
id: 2,
address: {
prov: 4,
},
}
}
},
created: function () {
console.log(this.$data)
}
}
// =>
{
title:"Component",
numberArr:[1,2,3,4,5],
objArr:[{id:1,name:1},{id:2,name:2},{id:3,name:3}],
user:{
address:{
city: "minix3333",
prov:4
},
id:2,
name:"Jack"
}
}
上面可以看出,对象类型的,会取组件对象的键值对,同名的以组件数据优先,但对象并不会做计算。
值为对象的选项,例如 methods
、 components
和 directives
,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
// Mixins.js file
export default {
methods: {
foo: function () {
console.log('foo')
},
conflicting: function () {
console.log('from mixin')
}
}
}
// home.vue file
import Mixins from "./Mixins.js"
export default {
mixins: [Mixins],
methods: {
bar: function () {
console.log('bar')
},
conflicting: function () {
console.log('from self')
}
}
}
// this.foo() // => "foo"
// this.bar() // => "bar"
// this.conflicting() // => "from self"
注意:Vue.extend() 也使用同样的策略进行合并。
自定义选项合并策略
自定义选项将使用默认策略,即简单地覆盖已有值。如果想让自定义选项以自定义逻辑合并,可以向 Vue.config.optionMergeStrategies
添加一个函数:
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
// 返回合并后的值
}
对于多数值为对象的选项,可以使用与 methods
相同的合并策略:
var strategies = Vue.config.optionMergeStrategies
strategies.myOption = strategies.methods
可以在 Vuex 1.x 的混入策略里找到一个更高级的例子:
const merge = Vue.config.optionMergeStrategies.computed
Vue.config.optionMergeStrategies.vuex = function (toVal, fromVal) {
if (!toVal) return fromVal
if (!fromVal) return toVal
return {
getters: merge(toVal.getters, fromVal.getters),
state: merge(toVal.state, fromVal.state),
actions: merge(toVal.actions, fromVal.actions)
}
}
Vue3下Mixins的变化
在vue3版本下,当mixin
和基类中data
合并时,会执行浅拷贝合并。
export default {
data () {
return{
title: "Mixin",
numberArr:[1,2,3,4,5,99,98],
objArr:[{id:'xxx',name:'xxx'},{id:'yyy',name:'yyy'},{id:3,name:'zzzz'}],
user: {
name: 'Jack',
id: "minix1",
address: {
prov: "minix2",
city: "minix3",
},
},
}
}
}
// home.vue file
import Mixins from "./Mixins.js"
export default {
mixins: [Mixins],
data(){
return{
title: "Component",
numberArr:[1,2,3,4,5],
objArr:[{id:1,name:1},{id:2,name:2},{id:3,name:3}],
user: {
id: 2,
address: {
prov: 4,
},
}
}
},
created: function () {
console.log(this.$data)
}
}
// vue2结果:
{
title:"Component",
numberArr:[1,2,3,4,5],
objArr:[{id:1,name:1},{id:2,name:2},{id:3,name:3}],
user:{
address:{
city: "minix3333",
prov:4
},
id:2,
name:"Jack"
}
}
// vue3结果:
{
title:"Component",
numberArr:[1,2,3,4,5],
objArr:[{id:1,name:1},{id:2,name:2},{id:3,name:3}],
user: {
id: 2,
address: {
prov: 4,
},
}
}
我们看到最后合并的结果,vue2.x会进行深拷贝,对data中的数据向下深入并合并拷贝;而vue3只进行浅层拷贝,对data中数据发现已存在就不合并拷贝。
小提示:Vue3中推荐使用Composition API中的自定义hook
Mixins和vuex的区别
vuex公共状态管理,在一个组件被引入后,如果该组件改变了vuex里面的数据状态,其他引入vuex数据的组件也会对应修改,所有的vue组件应用的都是同一个vuex数据。(在js中,有点类似于浅拷贝)
vue引入mixins数据,mixins数据或方法,在每一个组件中都是独立的,互不干扰的,都输入vue组件自身。(在js中,有点类似于深拷贝)
Mixins和公共组件的区别
- 通用的数据和方法,确实可以提出一个通用的组件,由父子组件传参的形式进行分享公用
- 公共组件最主要的作用还是复用相同的vue组件(有视图,有方法,有状态)
- 如果只是提取公用的数据或者方法,并且这些数据或者方法,不需要组件间进行维护,就可以使用mixins。(类似于js中封装的一些公用的方法)
- mixins 无法混入
export default
以外的内容,即只能混入vue相关的钩子函数和属性值。