Mixin
话说前头:
本质其实是一个js对象,它可以包含我们组件中任意功能选项,如data、components、methods、created、computed等等
我们只要将公共的功能以对象的方式传入mixins选项中,当组件使用mixins对象时所有mixins对象的选项都将被混入该组件本身的选项中来
vue中的混入主要分为全局混入和局部混入,我们日常开发比较常用局部混入,全局混入多用于编写插件
Tips:
- 如果mixin和组件内都调用相同生命周期钩子时,会先执行mixin的钩子,再执行组件的钩子
- 当组件存在与mixin对象相同的数据时,进行递归合并的时候组件的数据会覆盖mixin的数据(组件内部的优先级高)
1.mixin 使用
- mixins/index.ts 记录浏览页面时间
- Page.vue
2.mixin实现原理
- 优先递归处理 mixins
- 先遍历合并parent中的key,调用mergeFiled方法进行合并,然后保存在变量options
- 再遍历 child,合并补上parent 中没有的key, 调用mergeFiled方法进行合并,保存变量options
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (child.mixins) { // 判断有没有mixin 也就是mixin里面挂mixin的情况 有的话递归进行合并
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
const options = {}
let key
for (key in parent) {
mergeField(key) // 先遍历parent的key 调对应的strats[XXX]方法进行合并
}
for (key in child) {
if (!hasOwn(parent, key)) { // 如果parent已经处理过某个key 就不处理了
mergeField(key) // 处理child中的key 也就parent中没有处理过的key
}
}
function mergeField (key) {
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key) // 根据不同类型的options调用strats中不同的方法进行合并
}
return options
}
mixin分为四种策略
其实主要的逻辑就是合并mixin和当前组件的各种数据, 细分为四种策略:
* 替换型策略 - 同名的props、methods、inject、computed会被后来者代替
strats.props =
strats.methods =
strats.inject =
strats.computed = function (
parentVal: ?Object,
childVal: ?Object,
vm?: Component,
key: string
): ?Object {
if (!parentVal) return childVal // 如果parentVal没有值,直接返回childVal
const ret = Object.create(null) // 创建一个第三方对象 ret
extend(ret, parentVal) // extend方法实际是把parentVal的属性复制到ret中
if (childVal) extend(ret, childVal) // 把childVal的属性复制到ret中
return ret
}
* 合并型策略 - data, 通过set方法进行合并和重新赋值
strats.data = function(parentVal, childVal, vm) {
return mergeDataOrFn(
parentVal, childVal, vm
)
};
function mergeDataOrFn(parentVal, childVal, vm) {
return function mergedInstanceDataFn() {
var childData = childVal.call(vm, vm) // 执行data挂的函数得到对象
var parentData = parentVal.call(vm, vm)
if (childData) {
return mergeData(childData, parentData) // 将2个对象进行合并
} else {
return parentData // 如果没有childData 直接返回parentData
}
}
}
function mergeData(to, from) {
if (!from) return to
var key, toVal, fromVal;
var keys = Object.keys(from);
for (var i = 0; i < keys.length; i++) {
key = keys[i];
toVal = to[key];
fromVal = from[key];
// 如果不存在这个属性,就重新设置
if (!to.hasOwnProperty(key)) {
set(to, key, fromVal);
}
// 存在相同属性,合并对象
else if (typeof toVal =="object" && typeof fromVal =="object") {
mergeData(toVal, fromVal);
}
}
return to
}
* 队列型策略 - 生命周期函数和watch,原理是将函数存入一个数组,然后正序遍历依次执行
function mergeHook (
parentVal: ?Array<Function>,
childVal: ?Function | ?Array<Function>
): ?Array<Function> {
return childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
}
LIFECYCLE_HOOKS.forEach(hook => {
strats[hook] = mergeHook
})
* 叠加型策略 - component、directives、filters,通过原型链进行层层的叠加
strats.components=
strats.directives=
strats.filters = function mergeAssets(
parentVal, childVal, vm, key
) {
var res = Object.create(parentVal || null);
if (childVal) {
for (var key in childVal) {
res[key] = childVal[key];
}
}
return res
}