1. 题目与适用场景
订单状态通知提供“查看订单”“确认收货”“联系客服”三个 action。用户可能在应用关闭、已有窗口或登录过期时点击。请用 Notification action 的 navigate 设计流程,并说明浏览器自动导航与 notificationclick 自定义处理如何协作。
2. 面试官考察点
- 是否理解通知主体的
navigate与 action 的navigate是独立 URL,action URL 优先于事件回调。 - 是否知道没有 action URL 时才由
notificationclick处理,并能正确使用event.waitUntil。 - 是否能校验同源 URL、权限、登录状态和 Service Worker 生命周期。
- 是否能用 tag、业务幂等键和客户端恢复避免重复执行确认或打开多个窗口。
3. 回答前需要澄清的问题
- “确认收货”是否允许 GET 导航,还是必须经过 POST API 和二次确认?
- 未登录时是先打开登录页,还是把目标 action 暂存到登录后恢复?
- action 的目标是否允许外部客服域名?
- 已有窗口时应聚焦、发送消息,还是创建独立窗口?
4. 30 秒回答框架
我会把只读查看 action 映射到同源详情 URL,把有副作用的确认 action 留给 notificationclick,在 Service Worker 中调用带幂等键的 API,再导航到结果页。每个 URL 经过 allowlist 和权限校验;事件异步逻辑由 waitUntil 托管。已有窗口先聚焦并发送可信消息,失败时再打开回退页。
5. 分步骤深入解答
第一步:声明主体和 action URL
await self.registration.showNotification("订单 #123", {
body: "请选择操作",
tag: "order-123",
navigate: "/orders/123",
data: { orderId: "123", version: 4 },
actions: [
{ action: "view", title: "查看订单", navigate: "/orders/123" },
{ action: "confirm", title: "确认收货" },
{ action: "help", title: "联系客服", navigate: "/support/orders/123" },
],
});主体和 action 都应使用经过验证的同源 URL。tag 用于合并或更新同一订单通知,data 只携带恢复所需的非敏感标识。
第二步:区分无副作用和有副作用 action
view 和 help 只有导航,浏览器可直接处理。confirm 没有 navigate,因此进入 notificationclick;它必须调用服务端幂等 API,而不是把确认动作编码进 URL 查询参数。
第三步:实现 notificationclick 回退
self.addEventListener("notificationclick", (event) => {
event.notification.close();
const { orderId, version } = event.notification.data ?? {};
if (event.action !== "confirm" || !orderId) return;
event.waitUntil(confirmDelivery(orderId, version).then(() =>
focusOrOpen(`/orders/${encodeURIComponent(orderId)}?confirmed=1`)));
});生产代码还要捕获网络失败、过期版本和未授权响应,并把错误交给结果页展示。waitUntil 让 Service Worker 在 Promise 完成前保持事件存活。
第四步:处理窗口和登录恢复
focusOrOpen 应先匹配同源且受控的窗口,再通过 postMessage 传递经过验证的路径;没有可用窗口才调用 clients.openWindow。登录过期时只传递短期目标状态,登录完成后由页面重新读取订单并授权。
第五步:安全、权限与幂等
通知权限只决定能否展示通知,不代表订单授权。URL 必须限制协议、来源和路径;确认 API 使用订单号、版本或幂等键防止重复点击。取消通知、重复 tag 和多设备同时点击都应有明确的服务端状态机。
6. 高质量示范回答
我会把查看和帮助定义为同源navigate,把确认收货留给notificationclick,因为它有副作用。Service Worker 用waitUntil调用带订单版本和幂等键的确认 API,成功后聚焦已有窗口或打开结果页。所有 URL 做 allowlist 校验,权限不等于业务授权,登录恢复必须重新鉴权。测试覆盖主体点击、各 action、重复点击、离线、过期版本和多窗口。
7. 常见错误
- 用 GET URL 触发确认收货 → 预取或重复点击造成副作用 → 只在事件中调用幂等 POST API。
- 以为 action 没有 URL 会自动打开主体 URL → 行为不明确 → 在
notificationclick中显式处理。 - 把完整订单数据放入
data→ 暴露敏感信息 → 仅传短标识,页面重新请求授权数据。 - 异步操作不使用
waitUntil→ Worker 提前终止 → 托管所有关键 Promise。 - 每次都新建窗口 → 状态分裂 → 优先匹配并聚焦同源窗口。
8. 追问及应对
追问一:action.navigate 和 notificationclick 谁优先?
有 action 自己的 navigate 时,浏览器可按该 URL 导航;没有 URL 的 action 才需要 notificationclick 自定义处理。
追问二:为什么确认收货不能只放进 URL?
导航可能被预取、重放或重复点击,无法表达可靠的副作用语义。确认必须由服务端幂等 API 和授权状态机完成。
追问三:如何处理登录过期?
打开登录页时保存短期、不可篡改的目标状态;登录成功后由页面重新请求订单并检查权限,再恢复结果页。
追问四:如何验证不会重复执行?
用同一订单版本和幂等键连续触发主体、action、多窗口和多设备点击,断言服务端只产生一次确认状态转换。