/**
* 在对象上设置一个属性,且触发更新
*/functionset(target, key, val){// target未定义或是基本类型if(isUndef(target)||isPrimitive(target)){warn(("Cannot set reactive property on undefined, null, or primitive value: "+((target))));}// target是数组if(Array.isArray(target)&&isValidArrayIndex(key)){
target.length = Math.max(target.length, key);
target.splice(key,1, val);return val
}// target是对象,且key是对象已有的属性,所以已经存在getter/setterif(key in target &&!(key inObject.prototype)){
target[key]= val;return val
}// target的观察者实例var ob =(target).__ob__;// target是Vue实例if(target._isVue ||(ob && ob.vmCount)){warn('Avoid adding reactive properties to a Vue instance or its root $data '+'at runtime - declare it upfront in the data option.');return val
}if(!ob){
target[key]= val;return val
}defineReactive$$1(ob.value, key, val);
ob.dep.notify();return val
}/**
* 删除属性,且触发更新
*/functiondel(target, key){if(isUndef(target)||isPrimitive(target)){warn(("Cannot delete reactive property on undefined, null, or primitive value: "+((target))));}if(Array.isArray(target)&&isValidArrayIndex(key)){
target.splice(key,1);return}var ob =(target).__ob__;if(target._isVue ||(ob && ob.vmCount)){warn('Avoid deleting properties on a Vue instance or its root $data '+'- just set it to null.');return}if(!hasOwn(target, key)){return}delete target[key];if(!ob){return}
ob.dep.notify();}/**
* 当数组被获取时,收集对应的依赖(Watcher)
*/functiondependArray(value){for(var e =(void0), i =0, l = value.length; i < l; i++){
e = value[i];
e && e.__ob__ && e.__ob__.dep.depend();if(Array.isArray(e)){dependArray(e);}}}
第1162行~1568行:全局配置合并策略
/**
* 全局配置:自定义合并策略的选项。
*/var strats = config.optionMergeStrategies;/**
* 合并策略-获取默认值方法
*/{
strats.el = strats.propsData=function(parent, child, vm, key){if(!vm){warn("option \""+ key +"\" can only be used during instance "+'creation with the `new` keyword.');}// 获取默认值returndefaultStrat(parent, child)};}/**
* 合并2个对象,使用递归实现
*/functionmergeData(to,from){if(!from){return to }var key, toVal, fromVal;var keys = hasSymbol
? Reflect.ownKeys(from): Object.keys(from);for(var i =0; i < keys.length; i++){
key = keys[i];// 防止对象已经是响应式if(key ==='__ob__'){continue}
toVal = to[key];
fromVal = from[key];if(!hasOwn(to, key)){set(to, key, fromVal);}elseif(
toVal !== fromVal &&isPlainObject(toVal)&&isPlainObject(fromVal)){mergeData(toVal, fromVal);}}return to
}/**
* 合并对象或函数
*/functionmergeDataOrFn(parentVal,
childVal,
vm){if(!vm){if(!childVal){return parentVal
}if(!parentVal){return childVal
}// 合并函数returnfunctionmergedDataFn(){returnmergeData(typeof childVal ==='function'?childVal.call(this,this): childVal,typeof parentVal ==='function'?parentVal.call(this,this): parentVal
)}}else{// 合并实例对象函数returnfunctionmergedInstanceDataFn(){var instanceData =typeof childVal ==='function'?childVal.call(vm, vm): childVal;var defaultData =typeof parentVal ==='function'?parentVal.call(vm, vm): parentVal;if(instanceData){returnmergeData(instanceData, defaultData)}else{return defaultData
}}}}/**
* 合并策略-合并对象方法
*/
strats.data=function(parentVal,
childVal,
vm){if(!vm){if(childVal &&typeof childVal !=='function'){warn('The "data" option should be a function '+'that returns a per-instance value in component '+'definitions.',
vm
);return parentVal
}returnmergeDataOrFn(parentVal, childVal)}returnmergeDataOrFn(parentVal, childVal, vm)};/**
* 合并钩子数组
*/functionmergeHook(parentVal,
childVal){var res = childVal
? parentVal
? parentVal.concat(childVal): Array.isArray(childVal)? childVal
:[childVal]: parentVal;return res
?dedupeHooks(res): res
}/**
* 删除重复的钩子数组
*/functiondedupeHooks(hooks){var res =[];for(var i =0; i < hooks.length; i++){if(res.indexOf(hooks[i])===-1){
res.push(hooks[i]);}}return res
}/**
* 合并策略-生命周期钩子方法
*/LIFECYCLE_HOOKS.forEach(function(hook){
strats[hook]= mergeHook;});/**
* 合并资源
*/functionmergeAssets(parentVal,
childVal,
vm,
key){var res = Object.create(parentVal ||null);if(childVal){assertObjectType(key, childVal, vm);returnextend(res, childVal)}else{return res
}}/**
* 合并策略-合并资源类型方法
*/ASSET_TYPES.forEach(function(type){
strats[type +'s']= mergeAssets;});/**
* 合并策略-合并订阅者为数组类型
*/
strats.watch=function(parentVal,
childVal,
vm,
key){if(parentVal === nativeWatch){ parentVal =undefined;}if(childVal === nativeWatch){ childVal =undefined;}if(!childVal){return Object.create(parentVal ||null)}{assertObjectType(key, childVal, vm);}if(!parentVal){return childVal }var ret ={};extend(ret, parentVal);for(var key$1 in childVal){var parent = ret[key$1];var child = childVal[key$1];if(parent &&!Array.isArray(parent)){
parent =[parent];}
ret[key$1]= parent
? parent.concat(child): Array.isArray(child)? child :[child];}return ret
};/**
* 合并策略-其他对象方法
*/
strats.props =
strats.methods =
strats.inject =
strats.computed=function(parentVal,
childVal,
vm,
key){if(childVal &&"development"!=='production'){assertObjectType(key, childVal, vm);}if(!parentVal){return childVal }var ret = Object.create(null);extend(ret, parentVal);if(childVal){extend(ret, childVal);}return ret
};
strats.provide = mergeDataOrFn;/**
* 默认策略函数,childVal不存在,则取值parentVal
*/vardefaultStrat=function(parentVal, childVal){return childVal ===undefined? parentVal
: childVal
};/**
* 验证组件名
*/functioncheckComponents(options){for(var key in options.components){validateComponentName(key);}}/**
* 验证组件名是否合法
*/functionvalidateComponentName(name){if(!newRegExp(("^[a-zA-Z][\\-\\.0-9_"+(unicodeRegExp.source)+"]*$")).test(name)){warn('Invalid component name: "'+ name +'". Component names '+'should conform to valid custom element name in html5 specification.');}if(isBuiltInTag(name)|| config.isReservedTag(name)){warn('Do not use built-in or reserved HTML elements as component '+'id: '+ name
);}}/**
* 确保所有属性选项语法都标准化为基于对象的格式
*/functionnormalizeProps(options, vm){var props = options.props;if(!props){return}var res ={};var i, val, name;if(Array.isArray(props)){
i = props.length;while(i--){
val = props[i];if(typeof val ==='string'){
name =camelize(val);
res[name]={type:null};}else{warn('props must be strings when using array syntax.');}}}elseif(isPlainObject(props)){for(var key in props){
val = props[key];
name =camelize(key);
res[name]=isPlainObject(val)? val
:{type: val };}}else{warn("Invalid value for option \"props\": expected an Array or an Object, "+"but got "+(toRawType(props))+".",
vm
);}
options.props = res;}/**
* 所有injections(注射)都标准化为基于对象的格式
*/functionnormalizeInject(options, vm){var inject = options.inject;if(!inject){return}var normalized = options.inject ={};if(Array.isArray(inject)){for(var i =0; i < inject.length; i++){
normalized[inject[i]]={from: inject[i]};}}elseif(isPlainObject(inject)){for(var key in inject){var val = inject[key];
normalized[key]=isPlainObject(val)?extend({from: key }, val):{from: val };}}else{warn("Invalid value for option \"inject\": expected an Array or an Object, "+"but got "+(toRawType(inject))+".",
vm
);}}/**
* 原始函数指令都标准化为基于对象的格式
*/functionnormalizeDirectives(options){var dirs = options.directives;if(dirs){for(var key in dirs){var def$$1 = dirs[key];if(typeof def$$1 ==='function'){
dirs[key]={bind: def$$1,update: def$$1 };}}}}/**
* 验证资源是否为纯对象
*/functionassertObjectType(name, value, vm){if(!isPlainObject(value)){warn("Invalid value for option \""+ name +"\": expected an Object, "+"but got "+(toRawType(value))+".",
vm
);}}/**
* 将两个选项对象合并为一个新的。
* 用于实例化和继承的核心实用程序。
*/functionmergeOptions(parent,
child,
vm){{checkComponents(child);}if(typeof child ==='function'){
child = child.options;}normalizeProps(child, vm);normalizeInject(child, vm);normalizeDirectives(child);// 子选项扩展和混合。if(!child._base){if(child.extends){
parent =mergeOptions(parent, child.extends, vm);}if(child.mixins){for(var i =0, l = child.mixins.length; i < l; i++){
parent =mergeOptions(parent, child.mixins[i], vm);}}}var options ={};var key;for(key in parent){mergeField(key);}for(key in child){if(!hasOwn(parent, key)){mergeField(key);}}// 合并字段值functionmergeField(key){var strat = strats[key]|| defaultStrat;
options[key]=strat(parent[key], child[key], vm, key);}return options
}
第1575行~1760行:验证prop,解析prop
/**
* 解析资源。子实例需要访问父链中的资源
*/functionresolveAsset(options,
type,
id,
warnMissing){if(typeof id !=='string'){return}var assets = options[type];if(hasOwn(assets, id)){return assets[id]}var camelizedId =camelize(id);if(hasOwn(assets, camelizedId)){return assets[camelizedId]}var PascalCaseId =capitalize(camelizedId);if(hasOwn(assets, PascalCaseId)){return assets[PascalCaseId]}var res = assets[id]|| assets[camelizedId]|| assets[PascalCaseId];if(warnMissing &&!res){warn('Failed to resolve '+ type.slice(0,-1)+': '+ id,
options
);}return res
}/**
* 验证prop参数,为prop参数value赋默认值
*/functionvalidateProp(key,
propOptions,
propsData,
vm){var prop = propOptions[key];var absent =!hasOwn(propsData, key);var value = propsData[key];var booleanIndex =getTypeIndex(Boolean, prop.type);// 若prop.type(例如:String 或者 [String, Number])包含Boolean类型if(booleanIndex >-1){if(absent &&!hasOwn(prop,'default')){
value =false;}elseif(value ===''|| value ===hyphenate(key)){// value为空字符串或者是相同名称的booleanvar stringIndex =getTypeIndex(String, prop.type);// prop.type不包含stringIndex 或者 booleanIndex优先级比stringIndex高if(stringIndex <0|| booleanIndex < stringIndex){
value =true;}}}// 检查默认值if(value ===undefined){
value =getPropDefaultValue(vm, prop, key);// 此处默认值是一个新的拷贝,需调用observe方法,使value成响应式对象var prevShouldObserve = shouldObserve;toggleObserving(true);observe(value);toggleObserving(prevShouldObserve);}{assertProp(prop, key, value, vm, absent);}return value
}/**
* 从prop中获取默认值
*/functiongetPropDefaultValue(vm, prop, key){// 无default属性if(!hasOwn(prop,'default')){returnundefined}var def = prop.default;// prop为Object、Array的default需使用工厂函数返回对应的类型if(isObject(def)){warn('Invalid default value for prop "'+ key +'": '+'Props with type Object/Array must use a factory function '+'to return the default value.',
vm
);}// 原始prop值在渲染之前为undefined,则返回之前的默认值以避免不必要的订阅者触发if(vm && vm.$options.propsData &&
vm.$options.propsData[key]===undefined&&
vm._props[key]!==undefined){return vm._props[key]}// 如果def是函数且prop.type不是Function(即Object或Array),则使用call改变def的this指向,即调用工厂函数,返回对应的类型;// 否则直接返回def(默认值)returntypeof def ==='function'&&getType(prop.type)!=='Function'?def.call(vm): def
}/**
* 断言一个prop是否有效
*/functionassertProp(prop,
name,
value,
vm,
absent){// 验证必填propif(prop.required && absent){warn('Missing required prop: "'+ name +'"',
vm
);return}if(value ==null&&!prop.required){return}// 类型。例如:String,Booleanvar type = prop.type;var valid =!type || type ===true;var expectedTypes =[];if(type){if(!Array.isArray(type)){
type =[type];}// 由于每个prop,可能有多type([String, Number]),故使用for循环遍历正确的类型for(var i =0; i < type.length &&!valid; i++){var assertedType =assertType(value, type[i], vm);
expectedTypes.push(assertedType.expectedType ||'');
valid = assertedType.valid;}}var haveExpectedTypes = expectedTypes.some(function(t){return t;});if(!valid && haveExpectedTypes){warn(getInvalidTypeMessage(name, value, expectedTypes),
vm
);return}var validator = prop.validator;if(validator){if(!validator(value)){warn('Invalid prop: custom validator check failed for prop "'+ name +'".',
vm
);}}}var simpleCheckRE =/^(String|Number|Boolean|Function|Symbol|BigInt)$/;/**
* 断言类型
*/functionassertType(value, type, vm){var valid;var expectedType =getType(type);if(simpleCheckRE.test(expectedType)){var t =typeof value;
valid = t === expectedType.toLowerCase();// 原始类型的包装类型。例如:new String('小明')if(!valid && t ==='object'){
valid = value instanceoftype;}}elseif(expectedType ==='Object'){
valid =isPlainObject(value);}elseif(expectedType ==='Array'){
valid = Array.isArray(value);}else{try{
valid = value instanceoftype;}catch(e){warn('Invalid prop type: "'+String(type)+'" is not a constructor', vm);
valid =false;}}// 返回值valid:prop是否合法,expectedType:prop设置的类型,字符串格式。例如:{valid: true, expectedType: 'String'}return{valid: valid,expectedType: expectedType
}}
第1762行~1910行:工具函数
var functionTypeCheckRE =/^\s*function (\w+)/;/**
* 使用函数获取数据类型,用于对比组件的Props的参数。例如:getType(String)
*/functiongetType(fn){var match = fn && fn.toString().match(functionTypeCheckRE);return match ? match[1]:''}/**
* 类型是否相等
*/functionisSameType(a, b){returngetType(a)===getType(b)}/**
* 判断数据类型是否相等,不相等返回-1
*/functiongetTypeIndex(type, expectedTypes){if(!Array.isArray(expectedTypes)){returnisSameType(expectedTypes, type)?0:-1}for(var i =0, len = expectedTypes.length; i < len; i++){if(isSameType(expectedTypes[i], type)){return i
}}return-1}/**
* 获取非法类型提示
*/functiongetInvalidTypeMessage(name, value, expectedTypes){var message ="Invalid prop: type check failed for prop \""+ name +"\"."+" Expected "+(expectedTypes.map(capitalize).join(', '));var expectedType = expectedTypes[0];var receivedType =toRawType(value);// 检查是否需要指定期望值if(
expectedTypes.length ===1&&isExplicable(expectedType)&&isExplicable(typeof value)&&!isBoolean(expectedType, receivedType)){
message +=" with value "+(styleValue(value, expectedType));}
message +=", got "+ receivedType +" ";// 检查是否需要指定接受值if(isExplicable(receivedType)){
message +="with value "+(styleValue(value, receivedType))+".";}return message
}/**
* 风格化值
*/functionstyleValue(value, type){if(type ==='String'){return("\""+ value +"\"")}elseif(type ==='Number'){return(""+(Number(value)))}else{return(""+ value)}}varEXPLICABLE_TYPES=['string','number','boolean'];/**
* 是否可解释的类型
*/functionisExplicable(value){returnEXPLICABLE_TYPES.some(function(elem){return value.toLowerCase()=== elem;})}/**
* 至少有一个参数是boolean类型
*/functionisBoolean(){var args =[], len = arguments.length;while( len--) args[ len ]= arguments[ len ];return args.some(function(elem){return elem.toLowerCase()==='boolean';})}/**
* 处理错误函数
*/functionhandleError(err, vm, info){// 在处理错误处理程序时停用 deps 跟踪以避免可能的无限渲染。pushTarget();try{if(vm){var cur = vm;while((cur = cur.$parent)){var hooks = cur.$options.errorCaptured;if(hooks){for(var i =0; i < hooks.length; i++){try{var capture = hooks[i].call(cur, err, vm, info)===false;if(capture){return}}catch(e){globalHandleError(e, cur,'errorCaptured hook');}}}}}globalHandleError(err, vm, info);}finally{popTarget();}}/**
* 调用错误处理函数
*/functioninvokeWithErrorHandling(handler,
context,
args,
vm,
info){var res;try{
res = args ?handler.apply(context, args):handler.call(context);if(res &&!res._isVue &&isPromise(res)&&!res._handled){
res.catch(function(e){returnhandleError(e, vm, info +" (Promise/async)");});
res._handled =true;}}catch(e){handleError(e, vm, info);}return res
}/**
* 全局错误处理函数
*/functionglobalHandleError(err, vm, info){if(config.errorHandler){try{return config.errorHandler.call(null, err, vm, info)}catch(e){if(e !== err){logError(e,null,'config.errorHandler');}}}logError(err, vm, info);}/**
* 打印错误
*/functionlogError(err, vm, info){{warn(("Error in "+ info +": \""+(err.toString())+"\""), vm);}/* istanbul ignore else */if((inBrowser || inWeex)&&typeof console !=='undefined'){
console.error(err);}else{throw err
}}