代表性面试主题

前端面试题:通知 action 的 navigate 与 click 回退如何协作?

前端中等
Offer.cc 编辑团队发布 更新

题干

请设计一个包含查看、确认和帮助 action 的订单通知,并说明每个 action 的导航优先级、回退处理和幂等验证。

1. 题目与适用场景

订单状态通知提供“查看订单”“确认收货”“联系客服”三个 action。用户可能在应用关闭、已有窗口或登录过期时点击。请用 Notification action 的 navigate 设计流程,并说明浏览器自动导航与 notificationclick 自定义处理如何协作。

2. 面试官考察点

  • 是否理解通知主体的 navigate 与 action 的 navigate 是独立 URL,action URL 优先于事件回调。
  • 是否知道没有 action URL 时才由 notificationclick 处理,并能正确使用 event.waitUntil
  • 是否能校验同源 URL、权限、登录状态和 Service Worker 生命周期。
  • 是否能用 tag、业务幂等键和客户端恢复避免重复执行确认或打开多个窗口。

3. 回答前需要澄清的问题

  1. “确认收货”是否允许 GET 导航,还是必须经过 POST API 和二次确认?
  2. 未登录时是先打开登录页,还是把目标 action 暂存到登录后恢复?
  3. action 的目标是否允许外部客服域名?
  4. 已有窗口时应聚焦、发送消息,还是创建独立窗口?

4. 30 秒回答框架

我会把只读查看 action 映射到同源详情 URL,把有副作用的确认 action 留给 notificationclick,在 Service Worker 中调用带幂等键的 API,再导航到结果页。每个 URL 经过 allowlist 和权限校验;事件异步逻辑由 waitUntil 托管。已有窗口先聚焦并发送可信消息,失败时再打开回退页。

5. 分步骤深入解答

第一步:声明主体和 action URL

js
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

viewhelp 只有导航,浏览器可直接处理。confirm 没有 navigate,因此进入 notificationclick;它必须调用服务端幂等 API,而不是把确认动作编码进 URL 查询参数。

第三步:实现 notificationclick 回退

js
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、多窗口和多设备点击,断言服务端只产生一次确认状态转换。

公开来源

同类题目