11 #5

Merged
pamj3bxuk merged 6 commits from main into branch_yz 1 year ago

115
node_modules/fresh/index.js generated vendored

@ -30,108 +30,123 @@ module.exports = fresh
* @public
*/
// 判断缓存中的资源是否新鲜的函数,接收请求头和响应头作为参数
function fresh (reqHeaders, resHeaders) {
// fields
var modifiedSince = reqHeaders['if-modified-since']
var noneMatch = reqHeaders['if-none-match']
// 从请求头中获取 if-modified-since 字段的值,用于后续判断资源是否有修改
var modifiedSince = reqHeaders['if-modified-since'];
// 从请求头中获取 if-none-match 字段的值用于基于实体标签ETag验证资源是否变化
var noneMatch = reqHeaders['if-none-match'];
// unconditional request
if (!modifiedSince && !noneMatch) {
return false
// 如果 if-modified-since 和 if-none-match 字段都不存在,说明是无条件请求,直接返回 false表示资源不是新鲜的需重新获取资源
if (!modifiedSince &&!noneMatch) {
return false;
}
// Always return stale when Cache-Control: no-cache
// to support end-to-end reload requests
// https://tools.ietf.org/html/rfc2616#section-14.9.4
var cacheControl = reqHeaders['cache-control']
// 从请求头中获取 cache-control 字段的值,用于判断是否有 no-cache 指令
var cacheControl = reqHeaders['cache-control'];
// 如果 cache-control 字段存在且匹配表示 no-cache 的正则表达式(这里假设 CACHE_CONTROL_NO_CACHE_REGEXP 在外部已定义好),则返回 false意味着要忽略缓存重新获取资源遵循相关网络协议标准
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) {
return false
return false;
}
// if-none-match
if (noneMatch && noneMatch !== '*') {
var etag = resHeaders['etag']
// 如果 if-none-match 字段存在且不等于通配符 *,则进行基于 ETag 的验证
if (noneMatch && noneMatch!== '*') {
// 从响应头中获取 etag实体标签的值用于和请求头中的 if-none-match 进行匹配对比
var etag = resHeaders['etag'];
// 如果响应头中没有 etag则返回 false即资源不是新鲜的
if (!etag) {
return false
return false;
}
var etagStale = true
var matches = parseTokenList(noneMatch)
// 先假设基于 ETag 的验证是不通过的(资源是陈旧的),后续通过循环对比来更新这个状态
var etagStale = true;
// 调用 parseTokenList 函数解析 if-none-match 字段的值为一个令牌列表,方便后续逐个对比
var matches = parseTokenList(noneMatch);
for (var i = 0; i < matches.length; i++) {
var match = matches[i]
var match = matches[i];
// 将列表中的每个值与 etag 进行比较(考虑了弱验证等不同格式匹配情况,如 W/ 开头的弱验证格式),只要有匹配的就将 etagStale 设为 false表示资源是新鲜的基于 ETag 验证通过)
if (match === etag || match === 'W/' + etag || 'W/' + match === etag) {
etagStale = false
break
etagStale = false;
break;
}
}
// 如果经过遍历对比后,基于 ETag 的验证还是不通过etagStale 为 true则返回 false即资源不是新鲜的
if (etagStale) {
return false
return false;
}
}
// if-modified-since
// 如果 if-modified-since 字段存在,则进行基于资源最后修改时间的验证
if (modifiedSince) {
var lastModified = resHeaders['last-modified']
var modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince))
// 从响应头中获取 last-modified资源最后修改时间的值
var lastModified = resHeaders['last-modified'];
// 判断资源是否陈旧modifiedStale如果 lastModified 不存在或者通过 parseHttpDate 函数解析后的 lastModified 时间大于 modifiedSince 时间(即资源在 if-modified-since 所指定的时间之后有修改就认为资源是陈旧的modifiedStale 为 true
var modifiedStale =!lastModified ||!(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince));
// 如果资源是陈旧的modifiedStale 为 true则返回 false即资源不是新鲜的
if (modifiedStale) {
return false
return false;
}
}
return true
// 如果经过前面所有的验证条件都通过了,最后返回 true表示资源是新鲜的可以使用缓存
return true;
}
/**
* Parse an HTTP Date into a number.
*
* @param {string} date
* @private
*/
// Parse an HTTP Date into a number.
// 将一个 HTTP 日期格式的字符串解析为时间戳(数字形式),该函数是私有函数(可能只在内部使用)
function parseHttpDate (date) {
var timestamp = date && Date.parse(date)
// 尝试使用 Date.parse 方法解析传入的日期字符串,将解析结果赋给 timestamp 变量
var timestamp = date && Date.parse(date);
// istanbul ignore next: guard against date.js Date.parse patching
// 通过条件判断,如果 timestamp 的类型是 number就返回该时间戳否则返回 NaN处理 Date.parse 可能出现的解析失败情况,同时告诉测试框架(如 Istanbul忽略下面这行代码的覆盖情况可能因外部对 Date.parse 修改等特殊情况不好测试)
return typeof timestamp === 'number'
? timestamp
: NaN
? timestamp
: NaN;
}
/**
* Parse a HTTP token list.
*
* @param {string} str
* @private
*/
// Parse a HTTP token list.
// 解析一个类似 HTTP 令牌列表格式的字符串,该函数是私有函数(可能只在内部使用)
function parseTokenList (str) {
var end = 0
var list = []
var start = 0
var end = 0;
var list = [];
var start = 0;
// gather tokens
// 遍历字符串中的每个字符根据字符的编码来判断如何划分令牌token
for (var i = 0, len = str.length; i < len; i++) {
switch (str.charCodeAt(i)) {
case 0x20: /* */
// 如果字符编码是空格0x20且当前开始位置和结束位置相同说明连续的空格开头情况就更新开始和结束位置为下一个字符位置
if (start === end) {
start = end = i + 1
start = end = i + 1;
}
break
case 0x2c: /* , */
list.push(str.substring(start, end))
start = end = i + 1
break
break;
case 0x2c: /*, */
// 如果字符编码是逗号0x2c就将从 start 到 end 位置的子字符串作为一个令牌添加到 list 数组中,并更新 start 和 end 位置为下一个字符位置
list.push(str.substring(start, end));
start = end = i + 1;
break;
default:
end = i + 1
break
// 对于其他字符情况,就更新 end 位置到下一个字符
end = i + 1;
break;
}
}
// final token
list.push(str.substring(start, end))
// 将最后一个划分出来的令牌(从 start 到 end 位置的子字符串)添加到 list 数组中
list.push(str.substring(start, end));
return list
// 返回解析好的令牌列表数组,用于后续对类似 if-none-match 等字段值的进一步处理
return list;
}

@ -32,29 +32,37 @@ var hasOwnProperty = Object.prototype.hasOwnProperty
*/
function merge(dest, src, redefine) {
// 检查目标对象dest是否存在如果不存在则抛出类型错误表明目标对象参数是必需的
if (!dest) {
throw new TypeError('argument dest is required')
throw new TypeError('argument dest is required');
}
// 检查源对象src是否存在如果不存在则抛出类型错误表明源对象参数是必需的
if (!src) {
throw new TypeError('argument src is required')
throw new TypeError('argument src is required');
}
// 如果redefine参数未被传入即值为undefined则将其默认设置为true
if (redefine === undefined) {
// Default to true
redefine = true
redefine = true;
}
// 遍历源对象src自身的所有可枚举属性名不包括继承属性
Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) {
// 如果redefine为false并且目标对象dest已经拥有当前遍历到的属性名对应的属性
// 那么跳过该属性的处理,直接进入下一次循环
if (!redefine && hasOwnProperty.call(dest, name)) {
// Skip desriptor
return
return;
}
// Copy descriptor
var descriptor = Object.getOwnPropertyDescriptor(src, name)
Object.defineProperty(dest, name, descriptor)
})
// 获取源对象src中当前属性名对应的属性描述符包括属性值、可写性、可枚举性、可配置性等信息
var descriptor = Object.getOwnPropertyDescriptor(src, name);
// 使用获取到的属性描述符在目标对象dest上定义同名属性实现将源对象的属性复制到目标对象上
Object.defineProperty(dest, name, descriptor);
});
return dest
}
// 返回合并后的目标对象dest此时它已经包含了从源对象复制过来的属性根据redefine规则
return dest;
}
Loading…
Cancel
Save