题干与适用场景
一个离线优先网页需要在 Service Worker 中感知登录 Cookie 变化,并更新缓存策略。请设计 Cookie Store API 方案,说明订阅范围、事件语义、竞态和不支持浏览器的降级。
Cookie Store API 提供异步的 Cookie 读取与写入,并允许 Service Worker 订阅匹配的 Cookie 变化。它适合替代阻塞式的 document.cookie 读取,但不会改变 Cookie 的同源、路径、Secure、HttpOnly 或 SameSite 约束。
面试官考察点
重点包括:区分页面 API 与 Service Worker 的订阅 API、理解 cookiechange 只报告脚本可见变化、按名称和 URL 过滤、处理重复订阅与事件竞态,以及避免把 Cookie 当作可靠的跨上下文数据库。
澄清问题
先确认 Cookie 是否 HttpOnly、作用域是否跨路径、页面与 Service Worker 是否同源,以及目标浏览器支持情况。再问登录、登出和刷新是否可能并发发生,缓存更新是否必须实时,离线期间允许多长时间使用旧会话状态。
30 秒回答框架
“我会在 Service Worker 注册时订阅需要的 Cookie 名称和 URL,收到 cookiechange 后只重新读取相关 Cookie,并让缓存策略通过单调版本或会话状态机更新。Cookie Store API 是异步 API,事件只覆盖脚本可见的变化,也不提供事务或全局顺序保证;因此处理器必须幂等、去重并重新读取当前状态。对不支持的浏览器,我会保留网络请求中的 Cookie 校验和页面端显式同步,绝不把旧缓存当成已登录证明。”
分步骤深入解答
第一步:明确 API 边界
页面可通过 window.cookieStore 异步读取和写入 Cookie;Service Worker 则通过 registration.cookies 管理订阅,并接收 cookiechange 事件。两者都受浏览器 Cookie 策略约束,无法读取 HttpOnly Cookie 的值。
第二步:按最小范围创建订阅
订阅应指定必要的 Cookie 名称,并在确实需要时限定 URL。名称相同但路径不同的 Cookie 可能同时存在,处理器必须依据事件中的变更信息和当前 URL 重新确认目标,而不是只按名字猜测。
self.addEventListener("activate", (event) => {
event.waitUntil(
self.registration.cookies.subscribe([
{ name: "session", url: self.registration.scope },
]),
);
});第三步:理解事件不是值快照
事件包含变化的 Cookie 集合,但异步处理时 Cookie 可能已经再次变化。收到事件后应重新调用 get() 或 getAll() 读取当前状态,并把处理设计为幂等;不要把事件对象当作事务日志或最终真相。
第四步:区分脚本可见与 HttpOnly 变化
规范只要求脚本可见的 Cookie 变化触发相关通知。服务器设置或删除 HttpOnly Cookie 后,Service Worker 不能读取其值,也不应从缺少通知推断会话没有变化。认证结论仍需由服务器响应决定。
第五步:建立会话状态机
把状态定义为未知、已验证、已登出或过期,并使用响应中的版本、过期时间或重新验证结果推进状态。Cookie 变化只触发重新检查,不直接等同于登录成功或注销完成。
第六步:处理并发与跨标签页竞态
多个页面可能同时刷新或删除 Cookie。处理器应合并短时间内的通知,按当前读取结果执行一次缓存更新,并用会话版本避免旧事件覆盖新状态。缓存删除与预缓存写入也应具备幂等性。
第七步:设计不支持浏览器的降级
如果浏览器没有 Cookie Store API,页面可以在关键导航或网络响应后显式发送同步信号,服务器仍以 Cookie 验证为准。不要通过轮询 document.cookie 读取 HttpOnly 值,也不要因为 API 缺失就放宽缓存认证条件。
第八步:限制隐私和安全暴露
订阅只覆盖业务需要的名称与 URL,避免将 Cookie 值写入日志、Cache Storage 或 postMessage。设置 Cookie 时继续使用 Secure、HttpOnly、SameSite、Path 和合理的过期时间,并在登出时同时清理客户端缓存。
高质量示例答案
我会把 Cookie Store API 当作变化提示器,而不是认证数据库。Service Worker 激活时,为特定名称和作用域创建订阅;收到 cookiechange 后重新读取当前脚本可见状态,进入幂等的会话状态机,并通过会话版本更新或清理缓存。HttpOnly Cookie 的值始终由服务器响应验证,事件缺失也不代表会话没有变化。多个页面同时刷新时合并通知,避免旧事件覆盖新状态。对不支持的浏览器,保留关键请求的服务器校验与页面端显式同步,不能用 document.cookie 轮询替代 HttpOnly 保护。所有订阅和缓存操作都限制作用域,并覆盖登出、过期、离线和 Service Worker 更新场景。
常见误区
把 cookiechange 当成完整审计日志
事件是通知,不提供跨进程事务序列。处理器必须重新读取当前状态并支持重复执行。
认为 Service Worker 能读取 HttpOnly Cookie
HttpOnly 仍禁止脚本读取。Service Worker 只能通过网络请求让服务器验证会话,不能从 Cookie Store API 获得秘密值。
用 Cookie 变化直接决定缓存认证
Cookie 变化可能来自刷新、过期或不同路径。缓存策略应等待服务器验证和版本状态,不能把任意变化当作登录或登出结论。
延伸追问与参考答案
同名 Cookie 位于不同 Path 时如何避免误删?
在订阅和读取时保留 URL 作用域,删除时使用精确的 name、URL、Path 与其他属性。若无法确认目标 Cookie,就让服务器通过响应完成清理,不发送宽泛删除操作。
Service Worker 更新期间订阅会不会丢失?
在新的 Worker 的 activate 阶段幂等地确保订阅存在,并在迁移时读取当前 Cookie 状态重建缓存。不要依赖旧 Worker 的内存状态;更新完成后重新执行初始化。
离线时 Cookie 过期但没有网络,缓存该怎么办?
把本地状态标记为待验证并限制离线能力,不能延长服务器会话。恢复网络后先执行认证请求,再决定恢复、清理或降级缓存。