在Vue中,Mixin 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个 Mixin 对象可以包含任意组件选项。当组件使用 Mixin 对象时,所有 Mixin 对象的选项将被“混合”进入该组件本身的选项。
Mixin的特点
- 可以复用逻辑,并且变量是独立的,在组件中是独立的不会相互影响
- Mixin很容易造成命名冲突,因此在使用的时候必须要确保不会有冲突的属性名称,这样就会造成额外的起名负担~
Mixin的用法
全局用法
Vue.mixin({
data() {
return {
myOption: "hello"
};
},
created() {
console.log(this.myOption, 'this.myOption');
}
});
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
// 每一个子组件加载的时候都会打印出hello
// => "hello"
Mixin 也可以进行全局注册。使用时格外小心!一旦使用全局 mixin,它将影响每一个之后创建的组件 (例如,每个子组件)。
局部Mixin
const myMixin = {
data() {
return {
myOption: 'hello'
}
}
}
export defualt {
mixins:[myMixin],
created(){
console.log(this.myOption,'this.myOption');
}
}
// 这样只有在子组件中mixin的才会被合并
// => "hello"
当组件和 mixin 对象含有同名选项时,这些选项将以恰当的方式进行 “合并”。
Mixin的合并策略
在使用Mixin的时候不论是局部注册还是全局注册,在组件中如果有同名选项的时候,这些选项都会以一种方式进行合并,那么这种合并是如何而来的呢?又是如何定义的呢?这个时候就不得不看一下Vue源码中是如何定义Mixin的了。
Vue.mixin源码
// /src/core/global-api/mixin.js
import { mergeOptions } from '../util/index'
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
此时可以发现最主要的是用mergeOtpions
方法对基础的options
和mixin
进行了合并。
现在再来看mergeOptions
方法是如何实现的?
// /src/core/util/options.js
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
...
// 规范化属性
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
// 此处注释掉了一些需要递归判断mixin中存在mixin的情况
...
const options = {}
let key
for (key in parent) {
mergeField(key)
}
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
上面代码的主要含义: 1、递归处理mixin 2、遍历parent中的key,再调用mergeField进行合并,具体的合并策略在strats中
// src/core/util/options.js(依然会省略此处不关心的代码)
import config from '../config'
const strats = config.optionMergeStrategies
// 合并data
strats.data = function (
parentVal: any,
childVal: any,
vm?: Component
): ?Function {
// 暂不考虑是如何合并的
...
}
// 合并watch
strats.watch = function (
parentVal: ?Object,
childVal: ?Object,
vm?: Component,
key: string
): ?Object {
// 暂不考虑是如何合并的
...
}
基础的options
- 数据:data,props,propsData,computed,methods,watch
- DOM:el,template,render,renderError
- 生命周期钩子:
- 资源: directives、filters、components
- 组合: parent、extends、provide、inject
可以发现strats就是config.optionMrgeStrategies
,用这个来实现了的Vue内部的data,watch,props等的一些合并策略,而config.optionMergeStrategies
具体又是个什么呢?其实就是一个空对象而已,那么我们是否可以通过这个来自定义合并策略呢?答案是:当然可以
自定义合并策略
自定义选项在合并时,默认策略为简单地覆盖已有值。如果想让某个自定义选项以自定义逻辑进行合并,可以在 Vue.config.optionMergeStrategies
中添加一个函数:
Vue.config.optionMergeStrategies.customOption = (toVal, fromVal) => {
// return mergedVal
}
合并策略接收在父实例和子实例上定义的该选项的值,分别作为第一个和第二个参数。
Vue.config.optionMergeStrategies.myMixin = function (toVal, fromVal) {
console.log(toVal, fromVal);
// => undefined "parent"
// => parent reportList
return fromVal || toVal;
};
const myMixin = {
myMixin: 'parent'
};
export default {
myMixin: 'reportList',
mixins: [myMixin],
created(){
console.log(this.$options.myMixin);
// => reportList
}
}
虽然可以自定义合并策略,但是建议不要使用Vue中已有的options(例如data,props,watch,methods等)来合并,这样会冲突掉原来的逻辑,慎用的好!!!
总结
在 Vue 2 中,mixin 和合并策略是将部分组件逻辑抽象成可重用块的主要工具。但是,他们有几个问题:
-
mixin 很容易发生冲突:因为每个特性的属性都被合并到同一个组件中,所以为了避免 property 名冲突和调试,你仍然需要了解其他每个特性。
-
可重用性是有限的:我们不能向 mixin 传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑方面的灵活性。 为了解决这些问题,Vue3有了新方法:组合式 API,可以继续学习啦~
参考: