mergeField解析(构造函数)

发布于:2023-02-08 ⋅ 阅读:(2709) ⋅ 点赞:(3)


根据不同的配置项使用不同的方法合并配置,

mergeOptions 代码片段

var options = {}
var key

for(key in parent) {mergeField(key)}
for(key in child) {mergeField(key)}

function mergeField(key) {
	var strat = strats[key] || defaultStrat
	options[key] = strat(parent[key],child[key],vm)
}

strats 对象

starts 对象预制了一些方法用来处理一些固定的配置项

el 、propsData

这2个选项将使用默认合并方法defaultStrat 合并数据, 但是它们只有在创建实例的时候才有效, 如果不是在实例时合并,就会提示错误,但还是会合并

strats.el = strats.propsData = function(parent,child,vm,key){
	if(!vm) {...}
	return defaultStrat(parent,child)
}

data

合并data配置项,parentV就是 Vue.options.data , childV就是传入的data , 接着执行 mergeDataOrFn函数

strats.data = function(parentV,childV,vm) {
	if(!vm) {
		if(childV && typeof childV != "function") {
			return parentV
		}
		return mergeDataOrFn(parentV,childV)
	}
	return mergeDataOrFn(parentV,childV,vm)
}

mergeDataOrFn 分为2种合并,1. extend、mixin 合并和 2. 实例化时合并

  1. extend、mixin合并: 只要一侧传入的是undefined,就没有合并的必要,直接返回另一侧 data函数. 当两个参数都有值时,返回一个闭包函数 mergeDataFn ,它的参数就是各自的data数据
function mergeDataOrFn(parentV,childV,vm) {
	if(!vm) {
		if(!childV) {
			return parentV
		}
		if(!parentV) {
			return childV
		}
		return function mergeDataFn() {
			return mergeData(
				typeof childV === "function" ? childV.call(this,this) : childV,
				typeof parentV === "function" ? parentV.call(this,this) : parentV 
			)
		}
	} else {...}
}
  1. 实例化时传入了vm参数,则返回一个 mergedInstanceDataFn 闭包函数, 其实细看的话,和mergeDataFn差不多
if(!vm){...} else {
	return function mergedInstanceDataFn() {
		var instanceData = typeof childVal === 'function' ? childVal(vm,vm) : childVal
		var defaultData = typeof parentVal === 'function' ? parentVal(vm,vm) : parentVal
		if(instanceData) {
			return mergeData(instanceData,defaultData)
		} else {
			return defaultData
		}
	}
}

props、methods、inject、computed

这几个属性公用一个函数,功能是一致的.就是将 Vue.options上的和传入的配置项合并到一个空的对象中并返回 . 完整代码:

strats.props = 
strats.methods = 
strats.inject = 
strats.computed = function (parentV,childV,vm,key) {
	if(childV && "development" != "production") {
		assertObjectType(key,childV,vm)
	}
	if(!parentV) {return childV}
	var ret = Object.create(null)
	extend(ret,parentV)
	if(childV){extend(ret,childV)}
	return ret
}

assertObjectType 就是对配置项验证,格式必须为 [object Object]
如果 Vue.options 上没有对应的配置,则无需合并,直接返回传入的配置

if(!parentV) {return childV}

用一个空的对象来合并childV和parentV, 依次遍历属性添加到对象中

var ret = Object.create(null)
extend(ret,parentV)
if(childV) {extend(ret,childV)}
return ret

extend(to,from) 它的功能就是遍历对象,将from里的属性挨个添加到 to 上面

function extend(to,from) {
	for(var key in from) {
		to[key] = from[key]
	}
	return to
}

watch

先看看合并后的结果,如果两边都有数据,则合并后的watch是一个数组

watch: {
  test: [fn,fn]
}
  • 用户没有传入 watch选项时, 把Vue.options上的 watch 作为原型新建一个对象返回
  • Vue.options 上没有 watch选项时, 原样返回用户传入的watch配置
  • 两边都有时,先遍历Vue.options的watch把属性添加到临时变量ret上(又见extend函数,上面了解过它就是遍历对象把属性添加到另一个对象上,在这里也就是 ret对象),再遍历用户传入的配置
stras.watch =  function (parentV,childV,vm,key) {
  if(!childV) {return Object.create(parentV || null)}
  if(!parentV) {return childV}
  var ret = {}
  extend(ret,parentV)
  for(var key$1 in childV) {
    var parent = ret[key$1]
    var child = child[key$1]
    if(parent && !Array.isArray(parent)) {
      parent = [parent]
    }
    ret[key$1] = parent ? parent.concat(child) : Array.isArray(child) ? child : [child]
  }
  return ret
}

现在对遍历用户传入的配置做一下拆分,那么for-in的代码可以理解为:

for(var key in childV) {
  var parent = parentV[key]
  var child = childV[key]
  if(parent && !Array.isArray(parent)) {
    parent = [parent]
  }
  if(child && !Array.isArray(child)) {
    child = [child]
  }
  ret[key] = parent ? parent.concat(child): child
}

provide

provide 则使用与 data 合并时一样的 mergeDataOrFn函数, 只要有一侧没有 provide 就不会合并, 原样返回有数据的那一侧.否则返回 mergeDataFn 闭包函数,将在实例化的时候调用

strats.provide = mergeDataOrFn

components、directives、filters

这3个配置项将使用 mergeAssets 合并, 首先将 Vue.options 中对应的属性作为原型创建一个新的对象(之前Vue.extend的分析中了解到初始的Vue.options中是含有默认的 components、directives、filters),然后遍历用户传入的配置,依次添加到这个对象上

var res = Object.create(parent || null)
return extend(red,child)
/************************/
function extend(to,from) {
	for(var key in from) {
		to[key] = from[key]
	}
	return to
}

生命周期函数合并

生命周期函数统一使用 mergeHook 合并数据, 处理后的生命周期是一个数组

var LIFECYCLE_HOOLS = [
	'beforeCreate',
	'created',
	'beforeMount',
	'mounted',
	'beforeUpdate',
	'updated',
	'beforeDestroy',
	'destroyed',
	'activated',
	'deactivated'
]
LIFECYCLE_HOOLS.forEach(function(hook){
	strats[hook] = mergeHook
})

/************************/

function mergeHook(parentV,childV) {
	var res = childV ? (parentV ? parentV.concat(childV) : Array.isArray(childV) ? childV : [childV]) : [parentV]
	return res ? dedupeHooks(res) : res
}

为了简述这个三元表达式,我以 if() 表述帮助分析,大致如下:

  • 用户传入了某个生命周期函数并且Vue.options中也有一样的,则把用户传入的添加到数组的末尾.也就是这样形式
    created: [fn,fn]
    
  • 用户传入了但是Vue.options上没有,如果不是数组则包装成数组[ childV ],否则原样返回
  • 用户没有传入,但Vue.options上有,则把它包装成数组[ parentV ] 返回
var res = null
if(childV) {
	if(parentV) {
		res = parentV.concat(childV)
	} else {
		res = Array.isArray(childV) ? childV : [childV]
	}
} else {
	res = [parentV]
}

最后再调用去重方法 dedupHooks, 这部分就不分析了,代码也很简单

function dedupHooks(hooks) {
	var res = []
	for(var i = 0; i < hooks.length; i++) {
		if(res.indexOf(hooks[i] === -1)) {
			res.push(hooks[i])
		}
	}
	return res
}

defaultStrat 合并方法

除了 strats 预制的合并方法, 其余的都用 defaultStart 来处理合并, 它内部就是一个三元表达式, 用户有传入配置就使用用户传入的,否则使用Vue.options上的

function defaultStart(parentVal,childVal) {
  return childVal === undefind ? parentVal : childVal 
}

网站公告

今日签到

点亮在社区的每一天
去签到