vue2源码阅读 每日500行(一)

21 阅读2分钟
/*!  
* Vue.js v2.7.14  
* (c) 2014-2023 Evan You  
* Released under the MIT License.  
* 根据麻省理工学院许可证发布。  
*/  
(function (global, factory) {  
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :  
typeof define === 'function' && define.amd ? define(factory) :  
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Vue = factory());  
})  
(this, (  
  
function () {  
'use strict';  
var emptyObject = Object.freeze({});  
var isArray = Array.isArray;  
// These helpers produce better VM code in JS engines due to their  
// explicitness and function inlining.  
//由于它们的显式性和函数内联,这些助手可以在JS引擎中生成更好的VM代码。  
function isUndef(v) {  
return v === undefined || v === null;  
}  
function isDef(v) {  
return v !== undefined && v !== null;  
}  
function isTrue(v) {  
return v === true;  
}  
function isFalse(v) {  
return v === false;  
}  
/**  
* Check if value is primitive.  
* 检查值是否为原始类型。  
*/  
function isPrimitive(value) {  
return (typeof value === 'string' ||  
typeof value === 'number' ||  
// $flow-disable-line  
typeof value === 'symbol' ||  
typeof value === 'boolean');  
}  
function isFunction(value) {  
return typeof value === 'function';  
}  
/**  
* Quick object check - this is primarily used to tell  
* objects from primitive values when we know the value  
* is a JSON-compliant type.  
* 快速对象检查-当我们知道原始值是符合JSON的类型时,这主要用于区分对象和原始值。  
*/  
function isObject(obj) {  
return obj !== null && typeof obj === 'object';  
}  
/**  
* Get the raw type string of a value, e.g., [object Object].  
* 获取一个值的原始类型字符串,例如[object object]。  
*/  
var _toString = Object.prototype.toString;  
function toRawType(value) {  
return _toString.call(value).slice(8, -1);  
}  
/**  
* Strict object type check. Only returns true  
* for plain JavaScript objects.  
* 严格的对象类型检查。仅对普通JavaScript对象返回true。  
*/  
function isPlainObject(obj) {  
return _toString.call(obj) === '[object Object]';  
}  
function isRegExp(v) {  
return _toString.call(v) === '[object RegExp]';  
}  
/**  
* Check if val is a valid array index.  
* 检查val是否是有效的数组索引。  
*/  
function isValidArrayIndex(val) {  
var n = parseFloat(String(val));  
return n >= 0 && Math.floor(n) === n && isFinite(val);  
}  
function isPromise(val) {  
return (isDef(val) &&  
typeof val.then === 'function' &&  
typeof val.catch === 'function');  
}  
/**  
* Convert a value to a string that is actually rendered.  
* 将值转换为实际渲染的字符串。  
*/  
function toString(val) {  
return val == null ? '' : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)  
? JSON.stringify(val, null, 2)  
: String(val);  
}  
/**  
* Convert an input value to a number for persistence.  
* If the conversion fails, return original string.  
* 将输入值转换为数字以实现持久性。如果转换失败,则返回原始字符串。  
*/  
function toNumber(val) {  
var n = parseFloat(val);  
return isNaN(n) ? val : n;  
}  
/**  
* Make a map and return a function for checking if a key  
* is in that map.  
* 制作一个map并返回一个函数,用于检查钥匙是否在该map中。  
*/  
function makeMap(str, expectsLowerCase) {  
var map = Object.create(null);  
var list = str.split(',');  
for (var i = 0; i < list.length; i++) {  
map[list[i]] = true;  
}  
return expectsLowerCase ? function (val) {  
return map[val.toLowerCase()];  
} : function (val) {  
return map[val];  
};  
}  
/**  
* Check if a tag is a built-in tag.  
* 检查标记是否为内置标记。  
*/  
var isBuiltInTag = makeMap('slot,component', true);  
/**  
* Check if an attribute is a reserved attribute.  
* 检查属性是否为保留属性  
*/  
var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');  
/**  
* Remove an item from an array.  
* 从数组中删除一个项。  
*/  
function remove$2(arr, item) {  
var len = arr.length;  
if (len) {  
// fast path for the only / last item  
if (item === arr[len - 1]) {  
arr.length = len - 1;  
return;  
}  
var index = arr.indexOf(item);  
if (index > -1) {  
return arr.splice(index, 1);  
}  
}  
}  
/**  
* Check whether an object has the property.  
* 检查对象是否具有该属性  
*/  
var hasOwnProperty = Object.prototype.hasOwnProperty;  
function hasOwn(obj, key) {  
return hasOwnProperty.call(obj, key);  
}  
/**  
* Create a cached version of a pure function.  
* 创建一个纯函数的缓存版本。  
*/  
function cached(fn) {  
var cache = Object.create(null);  
return function cachedFn(str) {  
var hit = cache[str];  
return hit || (cache[str] = fn(str));  
};  
}  
/**  
* Camelize a hyphen-delimited string.  
* 将连字符分隔的字符串用骆驼形标记  
*/  
var camelizeRE = /-(\w)/g;  
var camelize = cached(function (str) {  
return str.replace(camelizeRE, function (_, c) { return (c ? c.toUpperCase() : ''); });  
});  
/**  
* Capitalize a string.  
* 将字符串首字母大写  
*/  
var capitalize = cached(function (str) {  
return str.charAt(0).toUpperCase() + str.slice(1);  
});  
/**  
* Hyphenate a camelCase string.  
* 将骆驼式字符串连字符。  
*/  
var hyphenateRE = /\B([A-Z])/g;  
var hyphenate = cached(function (str) {  
return str.replace(hyphenateRE, '-$1').toLowerCase();  
});  
/**  
* 用于不支持它的环境的简单绑定polyfill,例如PhantomJS 1.x。  
* 从技术上讲,我们不再需要它了,因为原生绑定现在在大多数浏览器中都足够高性能了。  
* 但删除它将意味着破坏能够在PhantomJS1.x中运行的代码,因此必须保留它以实现向后兼容性。  
*/  
/* istanbul ignore next */  
function polyfillBind(fn, ctx) {  
function boundFn(a) {  
var l = arguments.length;  
return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx);  
}  
boundFn._length = fn.length;  
return boundFn;  
}  
function nativeBind(fn, ctx) {  
return fn.bind(ctx);  
}  
// @ts-expect-error bind cannot be `undefined`  
var bind$1 = Function.prototype.bind ? nativeBind : polyfillBind;  
/**  
* 将类似Array的对象转换为真实的Array。  
*/  
function toArray(list, start) {  
start = start || 0;  
var i = list.length - start;  
var ret = new Array(i);  
while (i--) {  
ret[i] = list[i + start];  
}  
return ret;  
}  
/**  
* 将属性混合到目标对象中。  
*/  
function extend(to, _from) {  
for (var key in _from) {  
to[key] = _from[key];  
}  
return to;  
}  
/**  
* Merge an Array of Objects into a single Object.  
* 将一个对象数组合并为一个对象。  
*/  
function toObject(arr) {  
var res = {};  
for (var i = 0; i < arr.length; i++) {  
if (arr[i]) {  
extend(res, arr[i]);  
}  
}  
return res;  
}  
/* eslint-disable no-unused-vars */  
/**  
* Perform no operation.  
* Stubbing args to make Flow happy without leaving useless transpiled code  
* with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).  
* 不执行任何操作。Stubbing args使Flow高兴,而不会留下无用的传输代码。。。  
*/  
function noop(a, b, c) { }  
/**  
* Always return false.  
* 总是返回false  
*/  
var no = function (a, b, c) { return false; };  
/* eslint-enable no-unused-vars */  
/**  
* Return the same value.  
* 返回相同的值  
*/  
var identity = function (_) { return _; };  
/**  
* Generate a string containing static keys from compiler modules.  
* 从编译器模块生成包含静态键的字符串。  
*/  
function genStaticKeys$1(modules) {  
return modules  
.reduce(function (keys, m) {  
return keys.concat(m.staticKeys || []);  
}, [])  
.join(',');  
}  
/**  
* Check if two values are loosely equal - that is,  
* if they are plain objects, do they have the same shape?  
* 检查两个值是否大致相等——也就是说,如果它们是普通对象,它们的形状是否相同?  
*/  
function looseEqual(a, b) {  
if (a === b) return true;  
var isObjectA = isObject(a);  
var isObjectB = isObject(b);  
if (isObjectA && isObjectB) {  
try {  
var isArrayA = Array.isArray(a);  
var isArrayB = Array.isArray(b);  
if (isArrayA && isArrayB) {  
return (a.length === b.length &&  
a.every(function (e, i) {  
return looseEqual(e, b[i]);  
}));  
} else if (a instanceof Date && b instanceof Date) {  
return a.getTime() === b.getTime();  
} else if (!isArrayA && !isArrayB) {  
var keysA = Object.keys(a);  
var keysB = Object.keys(b);  
return (keysA.length === keysB.length &&  
keysA.every(function (key) {  
return looseEqual(a[key], b[key]);  
}));  
} else {  
/* istanbul ignore next */  
return false;  
}  
} catch (e) {  
/* istanbul ignore next */  
return false;  
}  
} else if (!isObjectA && !isObjectB) {  
return String(a) === String(b);  
} else {  
return false;  
}  
}  
/**  
* Return the first index at which a loosely equal value can be  
* found in the array (if value is a plain object, the array must  
* contain an object of the same shape), or -1 if it is not present.  
* 返回第一个索引,在该索引处可以在数组中找到松散相等的值(如果值是普通对象,则数组必须包含相同形状的对象),如果不存在,则返回-1。  
*/  
function looseIndexOf(arr, val) {  
for (var i = 0; i < arr.length; i++) {  
if (looseEqual(arr[i], val))  
return i;  
}  
return -1;  
}  
/**  
* Ensure a function is called only once.  
* 确保函数只被调用一次。  
*/  
function once(fn) {  
var called = false;  
return function () {  
if (!called) {  
called = true;  
fn.apply(this, arguments);  
}  
};  
}  
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#polyfill  
function hasChanged(x, y) {  
if (x === y) {  
return x === 0 && 1 / x !== 1 / y;  
} else {  
return x === x || y === y;  
}  
}  
  
var SSR_ATTR = 'data-server-rendered';  
var ASSET_TYPES = ['component', 'directive', 'filter'];  
var LIFECYCLE_HOOKS = [  
'beforeCreate',  
'created',  
'beforeMount',  
'mounted',  
'beforeUpdate',  
'updated',  
'beforeDestroy',  
'destroyed',  
'activated',  
'deactivated',  
'errorCaptured',  
'serverPrefetch',  
'renderTracked',  
'renderTriggered'  
];  
  
var config = {  
/**  
* Option merge strategies (used in core/util/options)  
*/  
// $flow-disable-line  
optionMergeStrategies: Object.create(null),  
/**  
* 是否取消显示警告  
*/  
silent: false,  
/**  
* 启动时显示生产模式提示消息?  
*/  
productionTip: true,  
/**  
* 是否启用devtools  
*/  
devtools: true,  
/**  
* 是否记录性能  
*/  
performance: false,  
/**  
* 观察程序错误的错误处理程序  
*/  
errorHandler: null,  
/**  
* 观察者警告的警告处理程序  
*/  
warnHandler: null,  
/**  
* 忽略某些自定义元素  
*/  
ignoredElements: [],  
/**  
* v-on的自定义用户密钥别名  
*/  
// $flow-disable-line  
keyCodes: Object.create(null),  
/**  
* 检查标记是否已保留,因此无法将其注册为组件。这取决于平台,可能会被覆盖。  
*/  
isReservedTag: no,  
/**  
* 检查是否保留了某个属性,使其不能用作组件道具。这取决于平台,可能会被覆盖。  
*/  
isReservedAttr: no,  
/**  
* 检查标记是否为未知元素。取决于平台。  
*/  
isUnknownElement: no,  
/**  
* 获取元素的命名空间  
*/  
getTagNamespace: noop,  
/**  
* 分析特定平台的真实标签名称。  
*/  
parsePlatformTagName: identity,  
/**  
* 检查是否必须使用属性绑定属性,例如依赖于平台的值。  
*/  
mustUseProp: no,  
/**  
* 异步执行更新。拟由Vue Test Utils使用如果设置为false,这将显著降低性能。  
*/  
async: true,  
/**  
* 因遗留原因而暴露  
*/  
_lifecycleHooks: LIFECYCLE_HOOKS  
};  
  
/**  
* unicode letters used for parsing html tags, component names and property paths.  
* using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname  
* skipping \u10000-\uEFFFF due to it freezing up PhantomJS  
* 用于解析html标记、组件名称和属性路径的unicode字母.  
*/  
var unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/;  
/**  
* Check if a string starts with $ or _  
* 检查字符串是否以$或_开头  
*/  
function isReserved(str) {  
var c = (str + '').charCodeAt(0);  
return c === 0x24 || c === 0x5f;  
}  
/**  
* Define a property.  
* 定义特性。  
*/  
function def(obj, key, val, enumerable) {  
Object.defineProperty(obj, key, {  
value: val,  
enumerable: !!enumerable,  
writable: true,  
configurable: true  
});  
}  
/**  
* Parse simple path.  
* 分析简单路径。  
*/  
var bailRE = new RegExp("[^".concat(unicodeRegExp.source, ".$_\\d]"));  
function parsePath(path) {  
if (bailRE.test(path)) {  
return;  
}  
var segments = path.split('.');  
return function (obj) {  
for (var i = 0; i < segments.length; i++) {  
if (!obj)  
return;  
obj = obj[segments[i]];  
}  
return obj;  
};  
}  
  
// can we use __proto__?  
//我们可以使用can we use __proto__  
var hasProto = '__proto__' in {};  
// Browser environment sniffing  
//浏览器环境嗅探  
var inBrowser = typeof window !== 'undefined';  
var UA = inBrowser && window.navigator.userAgent.toLowerCase();  
var isIE = UA && /msie|trident/.test(UA);  
var isIE9 = UA && UA.indexOf('msie 9.0') > 0;  
var isEdge = UA && UA.indexOf('edge/') > 0;  
UA && UA.indexOf('android') > 0;  
var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA);  
UA && /chrome\/\d+/.test(UA) && !isEdge;  
UA && /phantomjs/.test(UA);  
var isFF = UA && UA.match(/firefox\/(\d+)/);  
// Firefox has a "watch" function on Object.prototype...  
// @ts-expect-error firebox support  
var nativeWatch = {}.watch;  
var supportsPassive = false;  
if (inBrowser) {  
try {  
var opts = {};  
Object.defineProperty(opts, 'passive', {  
get: function () {  
/* istanbul ignore next */  
supportsPassive = true;  
}  
}); // https://github.com/facebook/flow/issues/285  
window.addEventListener('test-passive', null, opts);  
}  
catch (e) { }  
}