官网有以下解释:
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用。
其实就相当于注册了一些全局数据和方法,在注册混入之后的Vue实例中都可以使用。我们还是通过一个例子看看:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue demo</title>
</head>
<body>
<script src="./vue.js"></script>
<div id="app">
{{message}}
</div>
<div id="app2">
{{message}}
</div>
<script>
var mixinObj = {
methods: {
func1: function(){
console.log("mixinObj func1 is called");
this.message = this.message + " " + "Vue";
},
func2: function(){
console.log("mixinObj func2 is called");
this.message = this.message + " " + "China";
}
}
};
Vue.mixin(mixinObj);
const app = new Vue({
el:"#app",
data:{
message: "hello"
},
methods: {
func1: function(){
console.log("func1 is called");
this.message = this.message + " " + "World";
}
}
});
const app2 = new Vue({
el:"#app2",
data:{
message: "Hi"
},
methods: {
func3: function(){
console.log("func3 is called");
this.message = this.message + " " + "World";
}
}
});
settimeout(function(){
app.func1();
}, 3000);
settimeout(function(){
app2.func1();
}, 5000);
</script>
</body>
</html>
这个小例子运行后,首先刷新出两行字符Hello, Hi 过了3秒后变成Hello World , Hi ,过了5秒后变成Hello World, Hi Vue。
从页面的效果看,我们定义的mixinObj中的methods里面的方法确实混入了Vue实例中,并且如果Vue中的方法名重合的话会用Vue实例中的方法,也就是说如果重名就不混入。我们看看mixin方法是如何实现的。
function initMixin$1 (Vue) {
Vue.mixin = function (mixin) {
this.options = mergeOptions(this.options, mixin);
return this
};
}
Vue.mixin方法很简单,调用了mergeOptions方法,看这个方法之前我们先看看this.options 是什么。通过打印我们可以看到this.options里面的属性有components, filters, directives, _base。 这些属性是在initGlobal全局函数加入的:
Vue.options = Object.create(null);
ASSET_TYPES.forEach(function (type) {
Vue.options[type + 's'] = Object.create(null);
});
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue;
extend(Vue.options.components, builtInComponents);
var ASSET_TYPES = [
'component',
'directive',
'filter'
];
一目了然。我们接着看看mergeOptions函数:
function mergeOptions (
parent,
child,
vm
) {
{
checkComponents(child);
}
if (typeof child === 'function') {
child = child.options;
}
normalizeProps(child, vm);
normalizeInject(child, vm);
normalizeDirectives(child);
// Apply extends and mixins on the child options,
// but only if it is a raw options object that isn't
// the result of another mergeOptions call.
// Only merged options has the _base property.
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm);
}
if (child.mixins) {
for (var i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm);
}
}
}
var options = {};
var key;
for (key in parent) {
//把parent属性合入到options里面
mergeField(key);
}
for (key in child) {
if (!hasOwn(parent, key)) {
//如果parent属性中没有child的属性就把child的属性合入到options里面
mergeField(key);
}
}
function mergeField (key) {
var strat = strats[key] || defaultStrat;
options[key] = strat(parent[key], child[key], vm, key);
}
//最后返回parent 和 child合入的属性
return options
}
主要看看函数的后半部分,先定义一个options对象,对这个options进行操作最后return 这个options。这个过程最主要的是mergeField函数,strats定义了多个合并策略,我们看看methods合并策略:
strats.props =
strats.methods =
strats.inject =
strats.computed = function (
parentVal,
childVal,
vm,
key
) {
if (childVal && "development" !== 'production') {
assertObjectType(key, childVal, vm);
}
if (!parentVal) { return childVal } //如果父对象为空,直接返回子对象
var ret = Object.create(null);
extend(ret, parentVal); //把父对象拷贝到ret对象
if (childVal) { extend(ret, childVal); } //如果子对象存在,就把子对象拷贝到ret对象,如果子
//对象属性和ret中的对象属性相同,就覆盖
return ret
};
对methods策略进行了简单的注释。合并策略还是比较简单的。
接下来继续看我们这个小例子的mixin流程。我们知道mergeOptions方法的第一个参数this.options对象里面有components, filters, directives, _base 四个属性,第二个参数就是我们要合并的mixinObj,mixinOjb对象中只有我们定义的methods属性。所以调用Vue.mixin后在this.options对象上合入了methods属性。
在创建Vue实例时也会进行合并,我们看下:
Vue.prototype._init = function (options) {
var vm = this;
// a uid
vm._uid = uid$3++;
var startTag, endTag;
/* istanbul ignore if */
if (config.performance && mark) {
startTag = "vue-perf-start:" + (vm._uid);
endTag = "vue-perf-end:" + (vm._uid);
mark(startTag);
}
// a flag to avoid this being observed
vm._isVue = true;
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options);
} else {
vm.$options = mergeOptions( //在这里进行了合并
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
/* istanbul ignore else */
{
initProxy(vm);
}
// expose real self
vm._self = vm;
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created');
/* istanbul ignore if */
if (config.performance && mark) {
vm._name = formatComponentName(vm, false);
mark(endTag);
measure(("vue " + (vm._name) + " init"), startTag, endTag);
}
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
};
在创建Vue实例初始化时也会调用mergeOptions 在进行到:
for (key in parent) {
mergeField(key);
}
function mergeField (key) {
var strat = strats[key] || defaultStrat;
options[key] = strat(parent[key], child[key], vm, key); //这时,child[key] 不为空
//把传递给Vue实例的optoins中的methods func1方法覆盖了mixinObj中的methods
//func1方法
}
在合并父对象options时把Vue实例中的methods的func1属性 覆盖了mixinObj methods的func1属性。