# Mixins(混入)
## 基础
混入分发Vue组件可利用功能。一个混入的对象可以包含任意组件选项。当一个组件使用混入,所有选项都“混入”到组件自身选项中。
示例:
~~~
// 定义一个混入对象
const myMixin = {
created() {
this.hello()
},
methods: {
hello() {
console.log('hello from mixin!')
}
}
}
// 使用混入定义一个应用
const app = Vue.createApp({
mixins: [myMixin]
})
app.mount('#mixins-basic') // => "hello from mixin!"
~~~
## 选项合并
当一个混入和组件自身包含相同选项时,它们会以一种合适的方式“合并”。
例如冲突的data选项将会被合并,而且组件的data优先级更高。
~~~
const myMixin = {
data() {
return {
message: 'hello',
foo: 'abc'
}
}
}
const app = Vue.createApp({
mixins: [myMixin],
data() {
return {
message: 'goodbye',
bar: 'def'
}
},
created() {
console.log(this.$data) // => { message: "goodbye", foo: "abc", bar: "def" }
}
})
~~~
同名的勾子函数会被合并到一个数组中,所以它们都会被调用。混入的勾子函数会比组件自身的勾子函数先执行:
~~~
const myMixin = {
created() {
console.log('mixin hook called')
}
}
const app = Vue.createApp({
mixins: [myMixin],
created() {
console.log('component hook called')
}
})
// => "mixin hook called"
// => "component hook called"
~~~
选项期望值是对象,例如`methods`,`components`以及`directives`会被合并到同一个对象。当键名起冲突时组件选项优先级更高。
~~~
const myMixin = {
methods: {
foo() {
console.log('foo')
},
conflicting() {
console.log('from mixin')
}
}
}
const app = Vue.createApp({
mixins: [myMixin],
methods: {
bar() {
console.log('bar')
},
conflicting() {
console.log('from self')
}
}
})
const vm = app.mount('#mixins-basic')
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
~~~
## 全局混入
你也可以为应用定义全局的混入:
~~~
const app = Vue.createApp({
myOption: 'hello!'
})
//将`myOption`注入一个定制选项句柄
app.mixin({
created() {
const myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
app.mount('#mixins-global') // => "hello!"
~~~
谨慎使用!一旦你应用了一个全局混入,它将会影响整个应用的所有之后的组件实例创建(例如:子组件):
~~~
const app = Vue.createApp({
myOption: 'hello!'
})
// inject a handler for `myOption` custom option``
app.mixin({
created() {
const myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
// 在子组件中也加入myOption
app.component('test-component', {
myOption: 'hello from component!'
})
app.mount('#mixins-global')
// => "hello!"
// => "hello from component!"
~~~
在大部分情况下,你仅仅需要在定制选项上使用它,就像上面演示的例子。做为插件发布也是一个好主意,可以避免重复造轮子。
## 自定义选项合并策略
当自定义选项合并时,将使用默认的策略决定覆盖值。如果你想根据自己的逻辑来决定合并策略,你需要同`app.config.optionMergeStrategies`函数一起使用:
~~~
const app = Vue.createApp({})
app.config.optionMergeStrategies.customOption = (toVal, fromVal) => {
// 返回一个合并后的值
}
~~~
合并策略分别通过第一个和第二个入参接收父子组件定义的选项值,让我们验证下使用混入时这些参数都是什么:
~~~
const app = Vue.createApp({
custom: 'hello!'
})
app.config.optionMergeStrategies.custom = (toVal, fromVal) => {
console.log(fromVal, toVal)
// => "goodbye!", undefined
// => "hello", "goodbye!"
return fromVal || toVal
}
app.mixin({
custom: 'goodbye!',
created() {
console.log(this.$options.custom) // => "hello!"
}
})
~~~
正如所见,控制台首先打印混入的`toVal`和`fromVal`,然后才是应用。我们总是返回`fromVal`如果它被赋值,这就是为什么控制台最后会打印hello。最后,让我们改变一下策略,只返回子组件的值:
~~~
const app = Vue.createApp({
custom: 'hello!'
})
app.config.optionMergeStrategies.custom = (toVal, fromVal) => toVal || fromVal
app.mixin({
custom: 'goodbye!',
created() {
console.log(this.$options.custom) // => "goodbye!"
}
})
~~~
## 预防措施
在Vue2中,混入主要功能是抽取组件的逻辑部件到可复用的块中。但是有一些问题:
* 混入容易起冲突:自各功能的属性合并到同一个组件,你仍然需要知道每一个其它功能来避免组件名冲突和调试。
* 复用是有限制的:我们不能传递任何参数到混入来改变逻辑,这会降低抽逻辑协议的适用性。
提出这些问题,我们增加了一个新的方式来组织逻辑点代码:组合API。