vue中的mixin可以实现一些逻辑复用,我们来看看mixin的好处和一些不足。
优点:
- 可以提取一些功能(组件)的属性混合到另一个组件中或者全局对象中,灵活度高,耦合度低,便于维
缺点:
1.mixin中的方法以及逻辑不明确,不直观
2.因为过于灵活容易造成滥用
Mixin的实现方式
组件使用mixin的几种方法:
-Vue.mixin():直接调用组件构造函数上的mixin静态方法。
-可以通过mixins:['文件夹']来使用
Vue.mixin源码:
1 2 3 4 5 6 7 8 9 | // 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 } } |
在使用一个Vue.mixin传入一个对象,然后再调用mergeOptions方法传入一个基础的全局options和一个mixin进行合并
全局的基础options有这几个:
1 2 3 4 5 | export const ASSET_TYPES = [ // 资源类型 // 每一个Vue组件都会挂载的成员 'component', 'directive', 'filter' |
现在来看mergeOptions 里面的源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | export function mergeOptions (parent: Object, child: Object, vm?: Component): Object { if (process.env.NODE_ENV !== 'production') { checkComponents(child) } if (typeof child === 'function') { child = child.options } // normalize同字面意思一样,用来规范化属性 normalizeProps(child, vm) // 规范Inject normalizeInject(child, vm) normalizeDirectives(child) // 未合并的options不带有_base if (!child._base) { if (child.extends) { parent = mergeOptions(parent, child.extends, vm) } 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,在调用mergeField中的strats[key]进行合并 } for (key in child) { if (!hasOwn(parent, key)) { // 判断parent是否已经处理过了 mergeField(key) // 没有处理过,就在进行处理 } } function mergeField (key) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) // 根据不同类型的options调用strats不同的方法进行合并 } return options } |
上面代码的主要作用:
1.优先递归处理mixin
2.遍历合并parent中的key,在调用mergeField 进行合并,保存到变量options
核心在于strats中对应的不同类型的处理方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | strats.props = strats.methods = strats.inject = strats.computed = function ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): ?Object { if (childVal && process.env.NODE_ENV !== 'production') { assertObjectType(key, childVal, vm) } if (!parentVal) return childVal //判断parentVal,如果没有就返回parentVal const ret = Object.create(null) // 创建一个ret对象 extend(ret, parentVal) // extend方法实际是吧parentVal的属性复制到ret中 if (childVal) extend(ret, childVal) // 把childVal的属性复制到ret中 return ret } strats.provide = mergeDataOrFn |
props、methods、inject、computed的合并策略都是将新的同名参数替代旧的参数
在来看看data的合并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | strats.data = function (parentVal: any, childVal: any, vm?: Component): ?Function { return mergeDataOrFn(parentVal, childVal, vm) } mergeDataOrFn (parentVal: any, childVal: any, vm?: Component): ?Function { return function mergedInstanceDataFn () { const instanceData = typeof childVal === 'function' ? childVal.call(vm, vm) : childVal const defaultData = typeof parentVal === 'function' ? parentVal.call(vm, vm) : parentVal if (instanceData) { return mergeData(instanceData, defaultData) // 将2个对象进行合并 } else { return defaultData // instanceData没有,就直接返回defaultData } } } function mergeData (to: Object, from: ?Object): Object { if (!from) return to let key, toVal, fromVal const keys = hasSymbol ? Reflect.ownKeys(from) : Object.keys(from) for (let i = 0; i < keys.length; i++) { key = keys[i] // in case the object is already observed... if (key === '__ob__') continue toVal = to[key] fromVal = from[key] if (!hasOwn(to, key)) { // 不存在这个属性,就重新设置 set(to, key, fromVal) } else if ( toVal !== fromVal && isPlainObject(toVal) && isPlainObject(fromVal)) { // 存在相同属性,合并对象 mergeData(toVal, fromVal) } } return to } |
strats.data要遍历data中所有的属性,在根据不同的情况进行合并