We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
今天开始探索mixin的源码,我们可以知道mixin是什么时候进行合并的以及对各个类型是如何进行合并的。
我们先从Vue.mixin入手看源码
Vue.mixin
// vue/src/core/global-api/mixin.js export function initMixin (Vue: GlobalAPI) { Vue.mixin = function (mixin: Object) { this.options = mergeOptions(this.options, mixin) return this } }
我们在使用Vue.mixin的时候传入了一个对象,也就是源码中的形参mixin,然后调用mergeOptions方法将全局基础options和传入的mixin进行合并
mixin
mergeOptions
那么全局的基础options有什么呢?
// vue/src/core/global-api/index.js Vue.options = Object.create(null) ASSET_TYPES.forEach(type => { Vue.options[type + 's'] = Object.create(null) })
其中ASSET_TYPES是['component', 'directive', 'filter'],也就是全局的基础options是'component', 'directive', 'filter'
ASSET_TYPES
['component', 'directive', 'filter']
'component', 'directive', 'filter'
我们继续来看下mergeOptions方法是如何进行合并的(代码有删减)
// vue/src/core/util/options.js 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 }
上述代码的作用主要是这3点
优先递归处理 mixins
先遍历合并 parent 中的key,调用mergeField方法进行合并,然后保存在变量options
mergeField
再遍历 child,合并补上 parent 中没有的key,调用mergeField方法进行合并,保存在变量options
其实核心在于strats中对应的不同类型的处理方法,我们接下来分为几种类型来看下对应的合并策略
strats
props、methods、inject、computed属于替换式,我们来看下代码
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 } strats.provide = mergeDataOrFn
从上述代码可以看到props、methods、inject、computed的合并策略都是将新的同名参数替代旧的参数,也就是所说的替换型
data属于合并型,我们将源码简写一下
data
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 }
从代码可以看到这里遍历了要合并的 data 的所有属性,然后根据不同情况进行合并:
set方法其实就是一些合并重新赋值的方法,这里就不展开来说了
全部生命周期函数和watch都是队列型合并策略
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 })
代码比较简单,Vue 实例的生命周期钩子被合并为一个数组,然后正序遍历一次执行。watch的策略差不多,这里就不展开了
component、directives、filters属于叠加型的策略,但是业务中用的是真的少
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 }
通过原型链进行层层的叠加
代码有点多,如果还是不理解的话没有关系,我们来大致总结一下就好了
在我们调用Vue.mixin的时候会通过mergeOptions方法将全局基础options(component', 'directive', 'filter)进行合并
在mergeOptions内部优先进行mixins的递归合并,然后先父再子调用mergeField进行合并,不同的类型走不同的合并策略
替换型策略有props、methods、inject、computed, 就是将新的同名参数替代旧的参数
合并型策略是data, 通过set方法进行合并和重新赋值
set
队列型策略有生命周期函数和watch,原理是将函数存入一个数组,然后正序遍历依次执行
叠加型有component、directives、filters,将回调通过原理链联系在一起
The text was updated successfully, but these errors were encountered:
No branches or pull requests
【源码】mixin 源码解析
前言
今天开始探索mixin的源码,我们可以知道mixin是什么时候进行合并的以及对各个类型是如何进行合并的。
从源码看 Vue 中的 Mixin
我们先从
Vue.mixin
入手看源码我们在使用
Vue.mixin
的时候传入了一个对象,也就是源码中的形参mixin
,然后调用mergeOptions
方法将全局基础options和传入的mixin
进行合并那么全局的基础options有什么呢?
其中
ASSET_TYPES
是['component', 'directive', 'filter']
,也就是全局的基础options是'component', 'directive', 'filter'
我们继续来看下
mergeOptions
方法是如何进行合并的(代码有删减)上述代码的作用主要是这3点
优先递归处理 mixins
先遍历合并 parent 中的key,调用
mergeField
方法进行合并,然后保存在变量options再遍历 child,合并补上 parent 中没有的key,调用
mergeField
方法进行合并,保存在变量options其实核心在于
strats
中对应的不同类型的处理方法,我们接下来分为几种类型来看下对应的合并策略替换型
props
、methods
、inject
、computed
属于替换式,我们来看下代码从上述代码可以看到
props
、methods
、inject
、computed
的合并策略都是将新的同名参数替代旧的参数,也就是所说的替换型合并型
data
属于合并型,我们将源码简写一下从代码可以看到这里遍历了要合并的 data 的所有属性,然后根据不同情况进行合并:
set方法其实就是一些合并重新赋值的方法,这里就不展开来说了
队列型
全部生命周期函数和
watch
都是队列型合并策略代码比较简单,Vue 实例的生命周期钩子被合并为一个数组,然后正序遍历一次执行。
watch
的策略差不多,这里就不展开了叠加型
component
、directives
、filters
属于叠加型的策略,但是业务中用的是真的少通过原型链进行层层的叠加
总结
代码有点多,如果还是不理解的话没有关系,我们来大致总结一下就好了
在我们调用Vue.mixin的时候会通过
mergeOptions
方法将全局基础options(component', 'directive', 'filter)进行合并在
mergeOptions
内部优先进行mixins的递归合并,然后先父再子调用mergeField
进行合并,不同的类型走不同的合并策略替换型策略有
props
、methods
、inject
、computed
, 就是将新的同名参数替代旧的参数合并型策略是
data
, 通过set
方法进行合并和重新赋值队列型策略有生命周期函数和
watch
,原理是将函数存入一个数组,然后正序遍历依次执行叠加型有
component
、directives
、filters
,将回调通过原理链联系在一起The text was updated successfully, but these errors were encountered: