This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!
Although vue3 has been out for a long time, I think vue.js source code is still very worth learning. Vue. js inside the package of a lot of tools in our daily work projects will often be used. So I’ll be reading the vue.js source code and sharing snippets worth learning in the near future, and this article will continue to be updated.
I. What are the contents of 1200~2400 code? :
1. A series of merger strategies: (1) The combination strategy of Option EL and propsData PropsData ② The merge strategy of the data option strats.data ③ The merge strategy of the lifecycle hook option mergeHoo ④ the merge strategy of the assets option mergeAssets ⑤ the merge strategy of the watch option Struts. Props, struts. Methods, struts. Inject, strats.computed, All of them use the same merge policy function ⑦provide, which is the same as the data merge policy strats.provide ⑧ mergeOptions 2. NormalizeProps ① normalizeProps ②normalizeProps ②normalizeProps ③normalizeInject: NormalizeDirectives: Normalizes the properties of the Directives 3. Some warning information, exception capture, exception handling and so on
2. 1200-2400 lines of code
1. MutationObserver is the core of nextTick. The MutationObserver is the core of nextTick. The MutationObserver interface provides the ability to monitor changes made to the DOM tree. MutationObserver is also a microtask, so if the interviewer asks you what microtasks are there, is it a plus to be able to answer MutationObserver? 2. In the Vue project, if you need to obtain the modified DOM information, you need to create an asynchronous task after the DOM update task by nextTick. That is what the official website says :nextTick will perform a delayed callback after the end of the next DOM update loop.
3. Interpretation of 1200-2400 lines of code:
/** * Deletes properties and triggers changes if necessary */
function del (target, key) {
// Check whether the data is undefined or null
// Check whether the data type is string, number, symbol, Boolean
if (isUndef(target) || isPrimitive(target)
) {
warn(("Cannot delete reactive property on undefined, null, or primitive value: " + ((target))));
}
// Check whether it is an array and whether it is a valid array index
if (Array.isArray(target) && isValidArrayIndex(key)) {
// Add a new data to the end of the array
target.splice(key, 1);
return
}
// Declare an object ob with the value of all methods and properties on the stereotype in the target object, indicating that the data was added to the observer
var ob = (target).__ob__;
// If it is vue or check the number of times vue has been instantiated vmCount
if (target._isVue || (ob && ob.vmCount)) {
warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
);
return
}
// Check whether the object has this property
if(! hasOwn(target, key)) {return
}
delete target[key];
if(! ob) {return
}
// Trigger notification update, notifying subscriber obj.value to update data
ob.dep.notify();
}
DependArray () dependArray () dependArray () dependArray () dependArray () dependArray () dependArray () dependArray () dependArray
// Collect dependencies on array elements when touching an array, because we cannot intercept access to array elements like getters for properties.
function dependArray (value) {
for (var e = (void 0), i = 0, l = value.length; i < l; i++) {
e = value[i];
// Determine if __ob__ instances exist and call Depend for each to add wathcer management
e && e.__ob__ && e.__ob__.dep.depend();
if (Array.isArray(e)) {
// Recurse all the contents of the array until it is not an arraydependArray(e); }}}// Options el, propsData merge strategy
// The option override policy deals with how to merge the parent option value and the child option value into the final value.
/ / config. OptionMergeStrategies defines a merger strategy, in fact, the vue mixins properties
PropsData is used to pass data in the global extension
var strats = config.optionMergeStrategies
{
// Add two policies to the Strats policy object in the non-production environment: EL and propsData. Both attribute values are functions
// These two policy functions are used to merge the EL and propsData options
strats.el = strats.propsData = function (parent, child, vm, key) {
// If there is no pass
if(! vm) { warn("option \"" + key + "\" can only be used during instance " +
'creation with the `new` keyword.'
);
}
// In the production environment, the default policy function defaultStrat will be used directly to handle the el and propsData options
return defaultStrat(parent, child)
};
}
// We can see from the _init method that the vm in the policy function comes from the third argument of mergeOptions,
// The mergeOptions function is passed as the third argument, and the vm arguments are not retrieved from the policy. The mergeOptions function is called elsewhere except in the _init method
// MergeOptions is also called in the vue. extend method
// We call mergeOptions in vue. extend, but we don't pass the third parameter vm, so we can't get the VM in the policy.
// We can determine whether the mergeOptions function is instantiated using the _init method with the new operator or inherited from the vue. extend method
// Subclasses are created by instantiating subclasses. Subclasses are created by using the vue.extend method.
// If (! Vm) to determine whether it is a child component
// Merge two data objects recursively. To corresponds to the pure object generated by childVal, and from corresponds to the pure object generated by parentVal
function mergeData (to, from) {
// Return to if there is no form
if (!from) { return to }
var key, toVal, fromVal;
// If the type is Symbol
var keys = hasSymbol
? Reflect.ownKeys(from)// Return an array of all key attributes from
: Object.keys(from);// Return the key of the property from, excluding non-enumerable properties
// Loop through the array of the from attribute key
for (var i = 0; i < keys.length; i++) {
key = keys[i];
// If the current key is of type Object, continue
if (key === '__ob__') { continue }
toVal = to[key];
fromVal = from[key];
// Check whether the object has this property, and if so, set the fromVal property to[key]
if(! hasOwn(to, key)) { set(to, key, fromVal); }else if (
// Check whether toVal is created by "{}" or "new Object", and whether toVal! =fromVal
// This method is used to communicate with other JavaScript objects such as null, arrays, host objects (Documents),
//DOM, etc., since these all return objects with Typeof
toVal !== fromVal &&
isPlainObject(toVal) &&
isPlainObject(fromVal)
) {
mergeData(toVal, fromVal);
}
}
return to
}
// The mergeDataOrFn function always returns a function
function mergeDataOrFn (parentVal, childVal, vm) {
// If there is no VM, it is a child component option
if(! vm) {if(! childVal) {return parentVal
}
if(! parentVal) {return childVal
}
// When both parentVal and childVal exist, we need to return a function that returns the combined result of the two functions.
// Do not check whether parentVal is a function, because it must be a function passed previously merged.
return function mergedDataFn () {
return mergeData(
typeof childVal === 'function' ? childVal.call(this.this) : childVal,
typeof parentVal === 'function' ? parentVal.call(this.this) : parentVal
)
}
} else {
// The 'data' function is the 'mergedInstanceDataFn' function when the merge is handled with non-subcomponent options
return function mergedInstanceDataFn () {
// instance merge
var instanceData = typeof childVal === 'function'
? childVal.call(vm, vm)
: childVal;
var defaultData = typeof parentVal === 'function'
? parentVal.call(vm, vm)
: parentVal;
if (instanceData) {
return mergeData(instanceData, defaultData)
} else {
return defaultData
}
}
}
}
// Add the data policy function to the Strats policy object to merge the data options
strats.data = function (parentVal, childVal, vm) {
// When there is no VM parameter, it indicates that the child component options are being processed
if(! vm) {// Determine if childVal is a function
if (childVal && typeofchildVal ! = ='function') {
warn(
'The "data" option should be a function ' +
'that returns a per-instance value in component ' +
'definitions.',
vm
);
// Return parentVal if it is not a function
return parentVal
}
// The mergeDataOrFn method is called with or without the subcomponent option, but the third parameter vm is passed if it is a subcomponent option.
// This determines whether it is a child component option in the mergeDataOrFn function
return mergeDataOrFn(parentVal, childVal)
}
return mergeDataOrFn(parentVal, childVal, vm)
};
// Merge policy for lifecycle hook options
function mergeHook (parentVal, childVal) {
// The ternary operator: determines if there is a childVal parameter, and if there is a parentVal parameter,
// Merge childVal with parentVal if there is a parentVal parameter.
// If there is no parentVal, determine whether childVal is an array
// If it is an array type, return it directly. If it is not, convert childVal to an array, indicating that lifecycle hooks can be written as arrays and will be executed in array order
var res = childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal;
return res
? dedupeHooks(res)
: res
}
// This function is primarily used to handle the uniqueness of lifecycle hooks
function dedupeHooks (hooks) {
var res = [];
for (var i = 0; i < hooks.length; i++) {
if (res.indexOf(hooks[i]) === -1) { res.push(hooks[i]); }}return res
}
// The LIFECYCLE_HOOKS constant is actually an array of strings with the same name as the lifecycle hooks
LIFECYCLE_HOOKS.forEach(function (hook) {
strats[hook] = mergeHook;
});
// Merge strategy for the Assets option
function mergeAssets (parentVal, childVal, vm, key) {parentVal creates a new objectvar res = Object.create(parentVal || null);
// Check whether there is a childVal parameter
if (childVal) {
// The main reason is to determine whether childVal is a viable object
assertObjectType(key, childVal, vm);
return extend(res, childVal)
} else {
return res
}
}
// To iterate through ASSET_TYPES constants (component, directive, filter)
ASSET_TYPES.forEach(function (type) {
strats[type + 's'] = mergeAssets;
});
// The merge policy for the watch option. The merge handles the watch option
strats.watch = function (parentVal, childVal, vm, key) {
// In Firefox, object. prototype has a native watch function, which can be confusing.
// So when the component option is found to be the browser native Watch option, it means that the user did not provide the Vue watch option, directly reset to undefined
if (parentVal === nativeWatch) { parentVal = undefined; }
if (childVal === nativeWatch) { childVal = undefined; }
// If there is no childVal(that is, whether the component option has the watch option), parentVal is a new object
if(! childVal) {return Object.create(parentVal || null)} {// Determine whether childVal is a preserved object
assertObjectType(key, childVal, vm);
}
//parentVal returns childVal, which uses the component option directly
if(! parentVal) {return childVal }
var ret = {};
// Insert parentVal into the RET object
extend(ret, parentVal);
// If both parentVal and childVal exist, the merge process is required
for (var key$1 in childVal) {
var parent = ret[key$1];
var child = childVal[key$1];
if (parent && !Array.isArray(parent)) {
parent = [parent];
}
// If parent exists, merge child with parent. If parent does not exist, return child as an array
ret[key$1] = parent
? parent.concat(child)
: Array.isArray(child) ? child : [child];
}
return ret
};
/** * Combination policies for props, methods, inject, and computed */
strats.props =
strats.methods =
strats.inject =
strats.computed = function (parentVal, childVal, vm, key) {
// If there is a childVal parameter and the environment is not in production, we need to determine whether childVal is a preserved object
if (childVal && "development"! = ='production') {
assertObjectType(key, childVal, vm);
}
// If there is no parentVal parameter, then childVal is returned, using the corresponding component option directly
if(! parentVal) {return childVal }
var ret = Object.create(null);
extend(ret, parentVal);
if (childVal) { extend(ret, childVal); }
return ret
};
// The provide option has the same merge policy as data
strats.provide = mergeDataOrFn;
/* Use the default merge policy defaultStrat for the EL and propsData options. For the data option, the mergeDataOrFn function is used, and the end result is that the data option becomes a function whose execution result is the actual data object. For life cycle hook options, they are combined into an array so that both parent and child hook functions can be executed. For resource options such as directives, filters, and components, the parent and child options are processed in a prototype chain. This is why we can use built-in components, directives, and so on everywhere. For the watch option merge processing, similar to the lifecycle hook, if both the parent and child options have the same observation field, they will be merged into an array so that both observers will be executed. For the props, methods, inject, and computed options, the parent option is always available, but the child option overrides the parent option field with the same name. For the provide option, the merge policy uses the same mergeDataOrFn function as for the Data option. All options not mentioned above will make defaultStrat the default option. * /
var defaultStrat = function (parentVal, childVal) {
return childVal === undefined
? parentVal
: childVal
};
// Check whether the component name in the component parameter components of the Child object complies with the specification
function checkComponents (options) {
for (var key inoptions.components) { validateComponentName(key); }}// Check the component name
function validateComponentName (name) {
if (!new RegExp(("^[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 ); }}// Formalize the props attribute
function normalizeProps (options, vm) {
var props = options.props;
if(! props) {return }
var res = {};
var i, val, name;
{title: {type: null}} // If the props are an array, we'll loop the props and convert them to an object, for example :{title: {type: null}}
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.'); }}// Props are props
} else if (isPlainObject(props)) {
for (var key in props) {
val = props[key];
name = camelize(key);// Hump key
res[name] = isPlainObject(val)
? val
: { type: val };// Example :props: {type: String}}}else {
warn(
"Invalid value for option \"props\": expected an Array or an Object, " +
"but got " + (toRawType(props)) + ".",
vm
);
}
options.props = res;
}
// Normalize the inject attribute, similar to normalizeProps
function normalizeInject (options, vm) {
var inject = options.inject;
if(! inject) {return }
var normalized = options.inject = {};
// If inject is an array, we will loop inject and turn inject into an object, for example :{title: {form: null}}
if (Array.isArray(inject)) {
for (var i = 0; i < inject.length; i++) {
normalized[inject[i]] = { from: inject[i] };
}
// Loop inject if inject is a pure object
} else if (isPlainObject(inject)) {
for (var key in inject) {
var val = inject[key];
normalized[key] = isPlainObject(val)
? extend({ from: key }, val)
: { from: val };// inject: {'title': {from: 'title'}}}}else {
warn(
"Invalid value for option \"inject\": expected an Array or an Object, " +
"but got " + (toRawType(inject)) + ".", vm ); }}// Normalize the properties of the directives, for example :{change:{bind(){... },update(){... }}
function normalizeDirectives (options) {
var dirs = options.directives;
if (dirs) {
for (var key in dirs) {
var def$$1 = dirs[key];
//def$$1 must be function
if (typeof def$$1 === 'function') {
dirs[key] = { bind: def$$1, update: def$$1 }; }}}}// Determine whether it is a pure object
function assertObjectType (name, value, vm) {
if(! isPlainObject(value)) { warn("Invalid value for option \"" + name + "\": expected an Object, " +
"but got " + (toRawType(value)) + ".", vm ); }}// Merge policy function, parameter options merge parameters; Parent: parent instance parameter; Child: child instance parameter; Vm: Vue instance parameter, which is called during instantiation.
function mergeOptions (parent, child, vm) {{// Verify that the component name in the component parameter components of the Child object complies with the specification
checkComponents(child);
}
// Check whether child is a function
if (typeof child === 'function') {
child = child.options;
}
// Specifies the Props,Inject, and Directives
normalizeProps(child, vm);
normalize(child, vm);
normalizeDirectives(child);
// Only the merged options will have the _base attribute. Check if the child has the _base attribute. If the child has the _base attribute, it has been merged
if(! child._base) {// Merge extend and mixins with parent via mergeOptions
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;
// Iterates over the parent execution mergeField
for (key in parent) {
mergeField(key);
}
// Iterate over the child and execute the mergeField when the parent does not have a key
// If there is a key attribute, there is no need to merge it, because the previous step merged it into options
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key);
}
}
Options [key] = options[key] = options[key]
function mergeField (key) {
var strat = strats[key] || defaultStrat;
options[key] = strat(parent[key], child[key], vm, key);
}
return options
}
// To get the constructor defined in vue.extend
function resolveAsset (
options,
type,
id,
warnMissing
) {
if (typeofid ! = ='string') {
return
}
var assets = options[type];
// Check to see if there is an ID key in assets and return it if there is
if (hasOwn(assets, id)) { return assets[id] }
// Specifies the name of the hump of the key
var camelizedId = camelize(id);
// If the previous condition is not true, then convert the bond to a hump
if (hasOwn(assets, camelizedId)) { return assets[camelizedId] }
// Capitalize the first letter of the key after the hump
var PascalCaseId = capitalize(camelizedId);
// If the above conditions are not true, then convert the key to a hump and then convert the first letter to uppercase
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
}
// Check whether the data we pass meets the specification of prop
function validateProp (key, propOptions, propsData, vm) {
var prop = propOptions[key];// Get the values in propOptions
varabsent = ! hasOwn(propsData, key);// If propsData does not have this key, absent is true
var value = propsData[key];// Get the corresponding value in propsData
// Determine whether prop.type is a Boolean type
var booleanIndex = getTypeIndex(Boolean, prop.type);
if (booleanIndex > -1) {
// If absent is true and prop does not contain 'default'
if(absent && ! hasOwn(prop,'default')) {
value = false;
// If value is an empty string, get value equal to the key after the hump
} else if (value === ' ' || value === hyphenate(key)) {
// Determine whether prop.type is String
var stringIndex = getTypeIndex(String, prop.type);
// If it is of type stringIndex, or booleanIndex has a higher priority than stringIndex
if (stringIndex < 0 || booleanIndex < stringIndex) {
value = true; }}}// Call getPropDefaultValue to take the default value of prop when all values are equal to undefined
if (value === undefined) {
value = getPropDefaultValue(vm, prop, key);
//shouldObserve defaults to true
var prevShouldObserve = shouldObserve;
// Change shouldObserve to true
toggleObserving(true);
// Determine whether value is an object
observe(value);
// Change the shouldObserve value to the prevShouldObserve value
toggleObserving(prevShouldObserve);
}
{
assertProp(prop, key, value, vm, absent);
}
return value
}
// Get the default value of prop
function getPropDefaultValue (vm, prop, key) {
// Return undefined if the prop does not contain the 'default' property
if(! hasOwn(prop,'default')) {
return undefined
}
var def = prop.default;
// If def is of type object, a warning is issued
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
);
}
// Examples of props for vue: props for vue: props
// If the vm instance _props has the _props attribute, return the _props attribute
// This is an optimization done by vue. Since the function returns a new reference every time, when the component is updated,
// To avoid unnecessary watcher update
if (vm && vm.$options.propsData &&
vm.$options.propsData[key] === undefined&& vm._props[key] ! = =undefined
) {
return vm._props[key]
}
// Call def.call(VM) if function, otherwise return the value corresponding to the default property
return typeof def === 'function'&& getType(prop.type) ! = ='Function'
? def.call(vm)
: def
}
// Verify that the verification is successful. Verify required first and then type
function assertProp (prop, name, value, vm, absent) {
// If required, but if the current value is undefined, a warning is issued
if (prop.required && absent) {
warn(
'Missing required prop: "' + name + '"',
vm
);
return
}
// If it is not mandatory and value is null, return it directly
if (value == null && !prop.required) {
return
}
var type = prop.type;
var valid = !type || type= = =true;
var expectedTypes = [];
// Type must be an array type
if (type) {
if (!Array.isArray(type)) {
type = [type];
}
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 is false and expectedTypes have a value, then the value of the prop does not match the type defined by the prop, then a warning is printed
if(! valid && haveExpectedTypes) { warn( getInvalidTypeMessage(name, value, expectedTypes), vm );return
}
// Determine if there is a custom validator, execute if there is, and fire warn if the validator returns false
var validator = prop.validator;
if (validator) {
if(! validator(value)) { warn('Invalid prop: custom validator check failed for prop "' + name + '"., vm ); }}}// Props type check
var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/;
// Get the result of the assertion and break out of the loop until the traversal is complete or the value of valid is true
function assertType (value, type, vm) {
var valid;
// obtain the expectedType of prop, expectedType, and compare the value of prop with the expectedType
var expectedType = getType(type);
if (simpleCheckRE.test(expectedType)) {
var t = typeof value;
valid = t === expectedType.toLowerCase();
// Value must be one of the types in the type array
if(! valid && t ==='object') {
valid = value instanceof type;
}
// Determine whether value is a pure object
} else if (expectedType === 'Object') {
valid = isPlainObject(value);
} else if (expectedType === 'Array') {
// Check whether value is an array
valid = Array.isArray(value);
} else {
try {
// Check whether value is an array
valid = value instanceof type;
} catch (e) {
warn('Invalid prop type: "' + String(type) + '" is not a constructor', vm);
valid = false; }}return {
valid: valid,
expectedType: expectedType
}
}
// Function check
var functionTypeCheckRE = /^\s*function (\w+)/;
// Convert the fn argument to a string, and then look for function within the string
function getType (fn) {
var match = fn && fn.toString().match(functionTypeCheckRE);
return match ? match[1] : ' '
}
// Check whether it is the same type
function isSameType (a, b) {
return getType(a) === getType(b)
}
// Find the index whose type matches the expectedTypes and return it
function getTypeIndex (type, expectedTypes) {
if (!Array.isArray(expectedTypes)) {
return isSameType(expectedTypes, type)?0 : -1
}
for (var i = 0, len = expectedTypes.length; i < len; i++) {
if (isSameType(expectedTypes[i], type)) {
return i
}
}
return -1
}
// Generate a warning message
function getInvalidTypeMessage (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);
// Check whether you need to specify the receive value
if (
expectedTypes.length === 1 &&
isExplicable(expectedType) &&
isExplicable(typeofvalue) && ! isBoolean(expectedType, receivedType) ) { message +=" with value " + (styleValue(value, expectedType));
}
message += ", got " + receivedType + "";
// Check whether you need to specify the receive value
if (isExplicable(receivedType)) {
message += "with value " + (styleValue(value, receivedType)) + ".";
}
return message
}
function styleValue (value, type) {
if (type= = ='String') {
return ("\" " + value + "\" ")}else if (type= = ='Number') {
return ("" + (Number(value)))
} else {
return ("" + value)
}
}
var EXPLICABLE_TYPES = ['string'.'number'.'boolean'];
function isExplicable (value) {
// Convert value to lowercase and check if value is one of the types of 'string', 'number', or 'Boolean'
return EXPLICABLE_TYPES.some(function (elem) { returnvalue.toLowerCase() === elem; })}// Check whether the type is Boolean
function isBoolean () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; })}/* Catch exceptions */
// After getting the component that failed, you can recursively find the parent of the current component and call the errorCaptured method.
// Call the globalHandleError method after you have traversed all the errorCaptured methods, or if there is an error
function handleError (err, vm, info) {
// Deactivate deps tracking while processing error handler to avoid possible infinite rendering.
// See: https://github.com/vuejs/vuex/issues/1505
pushTarget();
try {
// Vm refers to the component instance that is currently reporting an error
if (vm) {
var cur = vm;
// Recursively find the current component's parent, calling the errorCaptured method in turn.
// Call the globalHandleError method after you have traversed all the errorCaptured methods, or if there is an error
while ((cur = cur.$parent)) {
var hooks = cur.$options.errorCaptured;
// Determine whether the errorCaptured hook function exists
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(); }}// Asynchronous error handling
function invokeWithErrorHandling (handler, context, args, vm, info) {
var res;
try {
// Select different processing functions for different parameters
res = args ? handler.apply(context, args) : handler.call(context);
if(res && ! res._isVue && isPromise(res) && ! res._handled) { res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); });
res._handled = true; }}catch (e) {
handleError(e, vm, info);
}
return res
}
// Call the global errorHandler method, which is output on the console using console.error in production environments
function globalHandleError (err, vm, info) {
// Get the global configuration and determine whether to set the handler
if (config.errorHandler) {
try {
// Execute the set global error handler
return config.errorHandler.call(null, err, vm, info)
} catch (e) {
// If the developer manually throws the same error message in the errorHandler function throw err to determine whether the err message is equal, avoid logging twice
// If a new error message is thrown, the log will be output together
if(e ! == err) { logError(e,null.'config.errorHandler');
}
}
}
logError(err, vm, info);
}
// Judge the environment and choose different throwing ways. When the browser or wechat environment and the type of console is un______, an error is printed
function logError (err, vm, info) {
{
warn(("Error in " + info + ": \" " + (err.toString()) + "\" "), vm);
}
if ((inBrowser || inWeex) && typeof console! = ='undefined') {
console.error(err);
} else {
throw err
}
}
// Next is the core code for nextTick
// Use the MicroTask identifier
var isUsingMicroTask = false;
var callbacks = [];
var pending = false;
// Execute callbacks synchronously
function flushCallbacks () {
pending = false;
var copies = callbacks.slice(0);
callbacks.length = 0;
for (var i = 0; i < copies.length; i++) { copies[i](); }}/* in previous versions of vue2.5, nextTick was basically based on micro tasks, but in some cases micro tasks have too high priority and may be between sequential events (e.g. # 4521, # 6690) or even between events bubbling during the same event (# 6566). However, changing everything to Macro Task will also have a performance impact on some scenes that have redrawn and animated, such as Issue #6813. The solution provided by vue versions after 2.5 is to use Micro Task by default, but to force macro Task */ when needed (such as in v-ON-attached event handlers)
var timerFunc;
// The nextTick behavior makes use of an accessible microtask queue
// Via native promise.then or MutationObserver.
//MutationObserver is more widely supported, but it is in
// UIWebView>=9.3.3 in iOS, when triggered in touch event handler, will completely stop working after several times
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise! = ='undefined' && isNative(Promise)) {
var p = Promise.resolve();
timerFunc = function () {
p.then(flushCallbacks);
// In the problematic UIWebViews, promise.then doesn't crash completely, but
// It may get into a strange state where the callback is pushed to
// Microtask queue but in the browser
// You need to do some other work, such as working with timers. So we can
// "force" refreshing the microtask queue by adding a timer.
if (isIOS) { setTimeout(noop); }}; isUsingMicroTask =true;
// The function of MutationObserver is to create an observer object that listens to a DOM element and executes the provided callback function when its DOM tree changes
} else if(! isIE &&typeofMutationObserver ! = ='undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// Use MutationObserver where native Promise is not available,
PhantomJS, iOS7, Android 4.4
// (#6466 MutationObserver is unreliable in IE11)
var counter = 1;
// Declare the MO and callback functions
var observer = new MutationObserver(flushCallbacks);
var textNode = document.createTextNode(String(counter));
// Listen for the textNode, and fire the nextTickHandler callback function if the text changes
observer.observe(textNode, {
characterData: true
});
// Each execution of timeFunc causes the text to switch between 1 and 0
timerFunc = function () {
counter = (counter + 1) % 2;
// Assign the data value to the data attribute. If the data attribute changes, the page will be rerendered
textNode.data = String(counter);
};
isUsingMicroTask = true;
} else if (typeofsetImmediate ! = ='undefined' && isNative(setImmediate)) {
// setImmediate uses macro tasks
timerFunc = function () {
setImmediate(flushCallbacks);
};
} else {
// If MutationObserver is not supported, use setTimeout
timerFunc = function () {
setTimeout(flushCallbacks, 0);
};
}
function nextTick (cb, ctx) {
var _resolve;
If cb is a function and CTX is an object, use cb.call(CTX).
callbacks.push(function () {
if (cb) {
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick'); }}else if(_resolve) { _resolve(ctx); }});// If pending is true, timerFunc(nextTickHandler, 0) has been executed in this round of event loop
if(! pending) { pending =true;
timerFunc();
}
// Check whether Promise objects exist
if(! cb &&typeof Promise! = ='undefined') {
return new Promise(function (resolve) { _resolve = resolve; }}})/ * * /
var mark;
var measure;
// This method is to take a snapshot of the time when the label was loaded in the browser
{
// In the browser environment
var perf = inBrowser && window.performance;
// The performance. Mark method creates a buffer in the browser's performance entry buffer with the given name,
// performanc.measure creates a name in the performance entry buffer between the browser's two specified tags, called the start and end tags, respectively
//performance. Measure removes the declared token from the browser's Performance Entry cache
if (
perf &&
perf.mark &&
perf.measure &&
perf.clearMarks &&
perf.clearMeasures
) {
mark = function (tag) { return perf.mark(tag); };
measure = function (name, startTag, endTag) {
perf.measure(name, startTag, endTag);
perf.clearMarks(startTag);
perf.clearMarks(endTag);
// perf.clearMeasures(name)}; }}/* not type checking this file because flow doesn't play well with Proxy */
// Initialize the proxy
var initProxy;
{
// The makeMap method cuts a string and puts it into a map. This method is used to verify whether a string exists in the map (case sensitive)
var allowedGlobals = makeMap(
'Infinity,undefined,NaN,isFinite,isNaN,' +
'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' +
'require' // for Webpack/Browserify
);
// Nonexistent, undefined attribute, method used to give warning
var warnNonPresent = function (target, key) {
warn(
"Property or method \"" + key + "\" is not defined on the instance but " +
'referenced during render. Make sure that this property is reactive, ' +
'either in the data option, or for class-based components, by ' +
'initializing the property. ' +
'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
target
);
};
// Check whether the attribute key is declared with a $or _ prefix. If it is, a warning is given
var warnReservedPrefix = function (target, key) {
warn(
"Property \"" + key + "\" must be accessed with \"$data." + key + "\" because " +
'properties starting with "$" or "_" are not proxied in the Vue instance to ' +
'prevent conflicts with Vue internals. ' +
'See: https://vuejs.org/v2/api/#data',
target
);
};
var hasProxy =
typeof Proxy! = ='undefined' && isNative(Proxy);
// Proxy is available in the current environment
if (hasProxy) {
// Whether a string exists in the map
var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact');
//config.keyCodes Custom key modifiers
config.keyCodes = new Proxy(config.keyCodes, {
set: function set (target, key, value) {
// Whether the key exists in the map
if (isBuiltInModifier(key)) {
warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key));
return false
} else {
target[key] = value;
return true}}}); }// This function is used to prompt the developer when the vm attributes are incorrectly called
var hasHandler = {
has: function has (target, key) {
var has = key in target;
var isAllowed = allowedGlobals(key) ||
(typeof key === 'string' && key.charAt(0) = = ='_' && !(key in target.$data));
if(! has && ! isAllowed) {if (key in target.$data) { warnReservedPrefix(target, key); }
else{ warnNonPresent(target, key); }}returnhas || ! isAllowed } };// The getHandler method is used to read a property of the proxy object.
// An error message is thrown when the accessed property is not a string or the property value does not exist on the object being propped, otherwise the property value is returned
var getHandler = {
get: function get (target, key) {
if (typeof key === 'string' && !(key in target)) {
if (key in target.$data) { warnReservedPrefix(target, key); }
else{ warnNonPresent(target, key); }}return target[key]
}
};
// Initialize the proxy
initProxy = function initProxy (vm) {
// Perform different processing logic by judging hasProxy
if (hasProxy) {
// If options has the render property and _withStripped has the render property,
// Use the getHandler method for the proxy's traps, otherwise use the hasHandler method
var options = vm.$options;
var handlers = options.render && options.render._withStripped
? getHandler
: hasHandler;
vm._renderProxy = new Proxy(vm, handlers);
} else{ vm._renderProxy = vm; }}; }var seenObjects = new _Set();
// Recursively iterate through the object to call all transformed getters, so that each nested property in the object is collected as a 'deep' dependency
// This function is mainly used for deep listening
function traverse (val) {
_traverse(val, seenObjects);
seenObjects.clear();
}
function _traverse (val, seen) {
var i, keys;
var isA = Array.isArray(val);
// Val is an array, frozen object, or VNode instance
if((! isA && ! isObject(val)) ||Object.isFrozen(val) || val instanceof VNode) {
return
}
// Only objects and arrays have an __ob__ attribute
if (val.__ob__) {
var depId = val.__ob__.dep.id;// Manually rely on the collector ID
if (seen.has(depId)) {
return
}
seen.add(depId);
}
if (isA) {
i = val.length;
// Recursively trigger the GET for each item to collect dependencies
while(i--) { _traverse(val[i], seen); }}else {
keys = Object.keys(val);
i = keys.length;
// Recursively trigger the get of the child attribute for dependency collection
while(i--) { _traverse(val[keys[i]], seen); }}}// This function is used to decompose an incoming event modifier with a specific prefix into an event object with a specific value
// Name attribute: event name
// The once property: whether to execute at one time
// Capture attribute: whether to capture the event
// Passive: whether to use the passive mode
var normalizeEvent = cached(function (name) {
var passive = name.charAt(0) = = ='&';
name = passive ? name.slice(1) : name;
var once$$1 = name.charAt(0) = = ='~';
name = once$$1 ? name.slice(1) : name;
var capture = name.charAt(0) = = ='! ';
name = capture ? name.slice(1) : name;
return {
name: name,
once: once$$1,
capture: capture,
passive: passive
}
});
// The implementation of the update listener with normalizeEvent is the actual execution of the event handler call
function createFnInvoker (fns, vm) {
function invoker () {
var arguments$1 = arguments;
// invoker. FNS is used to store the handler passed in
var fns = invoker.fns;
if (Array.isArray(fns)) {
var cloned = fns.slice();
for (var i = 0; i < cloned.length; i++) {
// Call the handler array
invokeWithErrorHandling(cloned[i], null, arguments$1, vm, "v-on handler"); }}else {
// A single handler call
return invokeWithErrorHandling(fns, null.arguments, vm, "v-on handler")
}
}
invoker.fns = fns;
return invoker
}
// This function is used to modify the listener configuration by iterating over the on event to bind to the new node and register the event to remove the listener from the old node
function updateListeners (
on,
oldOn,
add,
remove$$1,
createOnceHandler,
vm
) {
var name, def$$1, cur, old, event;
// Iterate over the on event to bind the registration event to the new node event
for (name in on) {
def$$1 = cur = on[name];
old = oldOn[name];
event = normalizeEvent(name);
if (isUndef(cur)) {
warn(
"Invalid handler for event \"" + (event.name) + "\": got " + String(cur),
vm
);
} else if (isUndef(old)) {
if (isUndef(cur.fns)) {
cur = on[name] = createFnInvoker(cur, vm);
}
if (isTrue(event.once)) {
cur = on[name] = createOnceHandler(event.name, cur, event.capture);
}
// Execute the actual registration event execution function, the implementation principle of add uses the native DOM addEventListener
add(event.name, cur, event.capture, event.passive, event.params);
} else if (cur !== old) {
old.fns = cur;
on[name] = old;
}
}
// Iterate over the old node and remove event listening on the old node
for (name in oldOn) {
if(isUndef(on[name])) { event = normalizeEvent(name); remove$$1(event.name, oldOn[name], event.capture); }}}// Save the insert hooks attribute to the Vnode's data, which will be invoked when the Vnode is inserted into the parent node
function mergeVNodeHook (def, hookKey, hook) {
if (def instanceof VNode) {
def = def.data.hook || (def.data.hook = {});
}
var invoker;
var oldHook = def[hookKey];
function wrappedHook () {
hook.apply(this.arguments);
// Remove the merged hook to ensure that it is only called once and to prevent memory leaks
remove(invoker.fns, wrappedHook);
}
if (isUndef(oldHook)) {
// There are no existing hooks
invoker = createFnInvoker([wrappedHook]);
} else {
if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {
// There is already an invoker (already a merged invoker)
invoker = oldHook;
invoker.fns.push(wrappedHook);
} else {
invoker = createFnInvoker([oldHook, wrappedHook]);
}
}
invoker.merged = true;
def[hookKey] = invoker;
}
// Get the original value
function extractPropsFromVNodeData (data, Ctor, tag) {
// Retrieve the props object for the component definition. Validation and default values are handled in the child component itself.
var propOptions = Ctor.options.props;
if (isUndef(propOptions)) {
return
}
var res = {};
var attrs = data.attrs;// Get the data attrs attribute
var props = data.props;// Get the props attribute of data
// If data has the attribute attrs or props
if (isDef(attrs) || isDef(props)) {
// To iterate over the props property of the component
for (var key in propOptions) {
// If key is a hump string, convert to - format
var altKey = hyphenate(key);
{
// Convert to lowercase
var keyInLowerCase = key.toLowerCase();
if( key ! == keyInLowerCase && attrs && hasOwn(attrs, keyInLowerCase) ) { tip("Prop \"" + keyInLowerCase + "\" is passed to component " +
(formatComponentName(tag || Ctor)) + ", but the declared prop name is" +
" \"" + key + "\"." +
"Note that HTML attributes are case-insensitive and camelCased " +
"props need to use their kebab-case equivalents when using in-DOM " +
"templates. You should probably use \"" + altKey + "\" instead of \"" + key + "\"."); }}// Call checkProp to retrieve the properties from props or attrs
checkProp(res, props, key, altKey, true) ||
checkProp(res, attrs, key, altKey, false); }}return res
}
Copy the code