上篇文章,咱们讲到了mergeOptions的部分实现,今天接着前面的部分讲解,来看代码,若是你们以为看讲解枯燥能够直接翻到本文的最后看mergeOptions的整个流程图。vue
const extendsFrom = child.extends if (extendsFrom) { parent = mergeOptions(parent, extendsFrom, vm) } if (child.mixins) { for (let i = 0, l = child.mixins.length; i < l; i++) { parent = mergeOptions(parent, child.mixins[i], vm) } }
这段代码的处理的逻辑是,当传入的options里有mixin或者extends属性时,再次调用mergeOptions方法合并mixins和extends里的内容到实例的构造函数options上(即parent options)好比下面这种状况ide
const childComponent = Vue.component('child', { ... mixins: [myMixin], extends: myComponent ... }) const myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin') } } } const myComponent = { mounted: function () { this.goodbye() }, methods: { goodbye: function () { console.log('goodbye from mixin') } } }
就会把传入的mounted, created钩子处理函数,还有methods方法提出来去和parent options作合并处理。
弄明白了这点咱们继续回到mergeOptions的代码函数
const options = {} let key
变量options存储合并以后的options,变量key存储parent options和child options上的key值。
接下来的部分算是mergeOptions方法的核心处理部分了,像炒菜同样,前面的代码至关于把全部的菜都配好了。接下来的部分就是教你怎么去炒菜了。this
for (key in parent) { mergeField(key) } for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } } function mergeField (key) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) }
前两段for循环代码大同小异,都是遍历options上的key值,而后调用mergeField方法来处理options。mergeField方法中出现了一个变量strats和defaultStrat。这两个变量存储的就是咱们的合并策略,也就是炒菜的菜谱,咱们先来看看defaultStratspa
const defaultStrat = function (parentVal: any, childVal: any): any { return childVal === undefined ? parentVal : childVal }
defaultStrat的逻辑是,若是child上该属性值存在时,就取child上的该属性值,若是不存在,则取parent上的该属性值。如今咱们知道默认的合并策略是什么了,接下来看其余的合并策略。咱们来看看strats里都有哪些属性?
上图就是strats中全部的策略了。粗略看起来里面的内容很是的多,若是细细分析会发现,其实总结起来无非就是几种合并策略。下面分别为你们介绍code
全部关于钩子函数的策略,其实都是调用mergeHook方法。component
function mergeHook ( parentVal: ?Array<Function>, childVal: ?Function | ?Array<Function> ): ?Array<Function> { return childVal ? parentVal ? parentVal.concat(childVal) : Array.isArray(childVal) ? childVal : [childVal] : parentVal }
mergeHook采用了一个很是骚的嵌套三元表达式来控制最后的返回值。下面咱们来解析这段三元表达式
(1) child options上不存在该属性,parent options上存在,则返回parent上的属性。对象
(2)child和parent都存在该属性,则返回concat以后的属性blog
(3)child上存在该属性,parent不存在,且child上的该属性是Array,则直接返回child上的该属性递归
(4) child上存在该属性,parent不存在,且child上的该属性不是Array,则把该属性先转换成Array,再返回。
上面就是钩子函数合并策略,结合图片看应该会比较清晰。
介绍完了钩子函数的合并策略,咱们接下来看props,methods,inject,computed等属性的合并策略。
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 const ret = Object.create(null) extend(ret, parentVal) if (childVal) extend(ret, childVal) return ret }
这个合并方法逻辑很简单,若是child options上这些属性存在,则先判断它们是否是对象。
(1)若是parent options上没有该属性,则直接返回child options上的该属性
(2)若是parent options和child options都有,则合并parent options和child options并生成一个新的对象。(若是parent和child上有同名属性,合并后的以child options上的为准)
components/directives/filters这几个属性的处理逻辑以下
function mergeAssets ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): Object { const res = Object.create(parentVal || null) if (childVal) { process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm) return extend(res, childVal) } else { return res } }
这里的处理逻辑和上一种状况的相似,这里不作过多讲解。
data和provide的策略相对来讲复杂一些,咱们先来看代码
export function mergeDataOrFn ( parentVal: any, childVal: any, vm?: Component ): ?Function { if (!vm) { // in a Vue.extend merge, both should be functions if (!childVal) { return parentVal } if (!parentVal) { return childVal } // when parentVal & childVal are both present, // we need to return a function that returns the // merged result of both functions... no need to // check if parentVal is a function here because // it has to be a function to pass previous merges. return function mergedDataFn () { return mergeData( typeof childVal === 'function' ? childVal.call(this, this) : childVal, typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal ) } } else { return function mergedInstanceDataFn () { // instance merge 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) } else { return defaultData } } } }
这个合并策略能够分红两种状况来考虑。
第一种状况,当前调用mergeOptions操做的是vm实例(调用new新建vue实例触发mergeOptions方法)
return function mergedInstanceDataFn () { // instance merge 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) } else { return defaultData } }
若是新建实例时传入的options上有data属性,则调用mergeData方法合并实例上的data属性和其构造函数options上的data属性(若是有的话)
第二种状况,当前调用mergeOptions操做的不是vm实例(即经过Vue.extend/Vue.component调用了mergeOptions方法)
if (!vm) { // in a Vue.extend merge, both should be functions if (!childVal) { return parentVal } if (!parentVal) { return childVal } // when parentVal & childVal are both present, // we need to return a function that returns the // merged result of both functions... no need to // check if parentVal is a function here because // it has to be a function to pass previous merges. return function mergedDataFn () { return mergeData( typeof childVal === 'function' ? childVal.call(this, this) : childVal, typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal ) } }
在这种状况下,其处理逻辑也是相似的。若是当前实例options或者构造函数options上有一个没有data属性,则返回另外一个的data属性,若是二者都有,则一样调用mergeData方法处理合并。
既然这两种状况都调用了mergeData方法,那咱们就继续来看看mergeData的源码
function mergeData (to: Object, from: ?Object): Object { if (!from) return to let key, toVal, fromVal const keys = Object.keys(from) for (let i = 0; i < keys.length; i++) { key = keys[i] toVal = to[key] fromVal = from[key] if (!hasOwn(to, key)) { set(to, key, fromVal) } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { mergeData(toVal, fromVal) } } return to }
mergeData的逻辑是,若是from对象中有to对象里没有的属性,则调用set方法,(这里的set就是Vue.$set,先能够简单理解为对象设置属性。以后会细讲)若是from和to中有相同的key值,且key对应的value是对象,则会递归调用mergeData方法,不然以to的值为准,最后返回to对象。这里咱们就讲完了data的合并策略。
返回mergeOptions代码里,在通过这几种合并策略合并options后,最终返回options
return options
讲到这里,整个mergeOptions的流程也讲完了。这个方法牵扯到的内容比较多,流程也比较复杂。为了你们更好的理解和记忆。我画了一张图来表达整个mergeOptions的过程。若是你们以为个人文章写的还行,请为我点赞,大家的承认是我最大的动力。