HTTP Cache-Control 与 ETag
先从一个浏览器可观察问题开始:同一操作为什么会改变节点、事件、请求、存储或辅助技术状态?本节把“HTTP Cache-Control 与 ETag”拆成输入、所有者、中间状态和输出,避免只背 API。
最小实验
下面的脚本不依赖外部站点。把它保存为 cache-http.mjs,用 Node 22 运行;第二次请求会命中同一个内存缓存,改动 etag 后才会重新取值。
js
const server = new Map([['/profile', { etag: 'v1', body: { name: 'Ada' } }]])
const cache = new Map()
async function request(path) {
const previous = cache.get(path)
const resource = server.get(path)
if (previous?.etag === resource.etag) return { source: 'cache', ...previous }
const next = { etag: resource.etag, body: structuredClone(resource.body) }
cache.set(path, next)
return { source: 'network', ...next }
}
console.log(await request('/profile')) // network, v1
console.log(await request('/profile')) // cache, v1
server.set('/profile', { etag: 'v2', body: { name: 'Grace' } })
console.log(await request('/profile')) // network, v2运行后应看到 network → cache → network。这里的 etag 只是实验中的缓存验证器;真实 HTTP 中由服务端通过 ETag 和条件请求决定 304,浏览器的 HTTP 缓存也不等同于 Cache API。
运行前记录环境和输入序列;运行后记录回调顺序、对象身份、可见文本、网络/存储变化或可访问名称。再改变一个条件:取消、卸载、重复触发、权限拒绝、跨源、键盘路径或不支持该 API 的浏览器。
规则与边界
- 平台保证、浏览器实现和业务约定必须分开;一个浏览器中的结果不能自动推广到所有宿主。
- 每个监听器、请求、观察器、缓存和焦点操作都要有清理/取消责任;页面或组件离开后不能继续写状态。
- 用户输入、URL、HTML、消息和持久化数据按不同信任边界处理;类型正确不代表内容可信。
- 失败、取消、超时、权限拒绝、配额不足和不支持要有不同的可观察结果。
练习
- 写出成功、失败、取消/卸载和降级四条路径的断言。
- 改变一个时序,解释哪条因果边发生变化。
- 说明一个替代 API 何时更适合及其代价。
下一步
完成答案核对后继续本章下一篇,并把实验日志保存在自己的项目中。