# Vue.mixin原理
# 实现原理
Vue.mixin源码位于src/core/global-api/mixin.js文件中,如下:
import { mergeOptions } from '../util/index'
// mixin主要用于options合并
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
2
3
4
5
6
7
8
mixin主要做了一件事儿,就是调用mergeOptions方法,将本身的mixin对象,与this.options对象合并,并赋值给this.options对象。
mergeOptions:主要作用就是options选项的合并,包括data、computed、methods、生命周期钩子等。
举例说明:
Vue.mixin({
a: 1,
b: 2
});
console.log(Vue.options); // 合并后的全局Vue.options
const App = {
template: '<div>app</div>'
};
const vm = new Vue({
name: 'root',
render: h => h(App)
}).$mount('#app');
console.log(vm.$options); // 当前vm的options选项
2
3
4
5
6
7
8
9
10
11
12
13
Vue.options值:

vm.$options值:
注意:vm.$options值,是调用mergeOptions方法,将Vue.options与vm自身的options合并后的结果。

# mixin作用
既然mixin可以将自身的options选项,与Vue.options合并。那么我们就可以将一些公共的方法、属性,或者生命周期通过mixin的方式,挂载Vue.options上。这样,无论是根实例,还是子组件实例,都可以通过vm.$options访问到。
根组件在
_init初始化时,会将Vue.options与自身options合并,并赋值给vm.$options子组件初始化之前,会调用Vue.extend方法返回一个子类Sub,该Sub类继承自父类Vue。在Vue.extend执行阶段。会调用
mergeOptions,将Vue.options与子组件选项options合并到Sub.options上。当子组件调用_init时,将vm.$options通过原型链,与Sub.options关联,参考源码src/core/instance/init.js:// Vue.extend Vue.extend = function (extendOptions: Object): Function { extendOptions = extendOptions || {} const Super = this const Sub = function VueComponent (options) { // 子组件初始化 this._init(options) } // 继承Vue Sub.prototype = Object.create(Super.prototype) Sub.prototype.constructor = Sub // 将Vue.options与自身extendOptions合并 Sub.options = mergeOptions( Super.options, extendOptions ) return Sub } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// init.js export function initInternalComponent (vm: Component, options: InternalComponentOptions) { // ... const opts = vm.$options = Object.create(vm.constructor.options); // ... }1
2
3
4
5
6
mixin的一个典型场景,就是在vue-router源码中,通过mixin的方式混入一个全局的beforeCreate函数:
let Vue;
VueRouter.install = (_Vue) => {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
if (this.$options.router) { // 根组件
this._routerRoot = this;
this._router = this.$options.router;
// router初始化
// this._router.init(this);
} else {
// 子组件可以通过 this._routerRoot._router
this._routerRoot = this.$parent && this.$parent._routerRoot;、
}
}
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
当我们调用Vue.use(VueRouter)时,会执行其install方法,该方法会在全局的Vue.options上填加一个beforeCreated对象,其值是一个数组,数组中的每一项就是对应的fn钩子函数。

当我们在子组件中也有beforeCreated钩子时,这2个钩子会合并到一个数组中,且顺序为[父钩子,子钩子]。因此,当我们在根实例的选项上注入了router实例后,
详细的合并策略,可以参看mergeOptions中关于生命周期钩子的合并mergeHook (opens new window)。

# 缺点
mixin优点很明显,就是可以抽取公共逻辑,进行代码复用。也可以通过一些钩子,做一些特定的逻辑。但是其缺点也很明显:
- 数据来源不明
- mixin中可能出现命名冲突