代表性面试主题

前端面试题:如何用 Notification.navigate 设计可靠的推送深链?

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

题干

请设计一条从推送通知进入订单详情的深链流程,并解释 navigate 与 notificationclick、action.navigate、权限和跨窗口复用的边界。

1. 题目与适用场景

电商 Web Push 通知需要把用户带到订单详情。通知可能在应用未打开、已有应用窗口或用户点击某个 action 时触发。请使用 navigate 选项设计深链,并说明 URL 解析、权限、点击回退、重复打开和服务 Worker 生命周期。

2. 面试官考察点

  • 是否知道 NotificationOptions.navigate 是导航 URL,Notification.navigate 只读返回解析后的绝对 URL 或空字串。
  • 是否能区分通知主体导航、action 导航和 notificationclick 事件回退。
  • 是否处理权限、HTTPS、同源策略、开放重定向、重复通知和已有窗口复用。
  • 是否能把业务路由恢复、鉴权和幂等放在应用层,而非依赖浏览器自动导航完成。

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

  1. 通知由页面脚本还是 Service Worker 的 showNotification 创建?
  2. 深链是否允许外部 URL,订单详情需要登录后如何恢复?
  3. 用户点击通知主体与 action 时,是否要进入不同页面或执行不同动作?
  4. 已有同源窗口时,产品希望聚焦并复用,还是创建新窗口?

4. 30 秒回答框架

我会在 HTTPS 下由 Service Worker 创建带 navigate 的持久通知,并只写入经过 allowlist 校验的同源 URL。点击主体或 action 时,浏览器可导航到对应 URL;若 action 没有自己的 navigate,在 notificationclick 中处理业务回退,先聚焦已有窗口,再通过客户端路由恢复订单。权限被拒绝、URL 无效或鉴权失败都要有明确的应用层兜底。

5. 分步骤深入解答

第一步:构造经过校验的通知 URL

js
const target = new URL(`/orders/${orderId}`, self.location.origin);

await self.registration.showNotification("订单已发货", {
  body: "点击查看物流进度",
  tag: `order-${orderId}`,
  navigate: target.href,
  data: { orderId },
});

navigate 会按通知创建时的 base URL 解析。服务端下发的路径必须经过同源和路由 allowlist 校验,不能把任意用户输入直接当作跳转地址。

第二步:区分主体和 action 导航

js
await self.registration.showNotification("订单待确认", {
  body: "请选择操作",
  navigate: "/orders/123",
  actions: [
    { action: "open", title: "查看订单", navigate: "/orders/123" },
    { action: "help", title: "联系客服" },
  ],
});

点击 action 时,action 自己的 navigate 优先;没有该 URL 时才进入 notificationclick 事件逻辑。主体导航和 action 导航都要使用同一套 allowlist 与鉴权恢复规则。

第三步:处理点击回退和已有窗口

js
self.addEventListener("notificationclick", (event) => {
  event.notification.close();
  if (event.action === "help") {
    event.waitUntil(clients.openWindow("/support"));
    return;
  }
  event.waitUntil(clients.matchAll({ type: "window", includeUncontrolled: true })
    .then((windows) => windows[0]?.focus() ?? clients.openWindow("/orders/123")));
});

实际应用应检查窗口 URL、等待 Service Worker 控制和传递可信的 data,不要固定写死订单号。浏览器实现可以复用或新建顶层窗口,产品逻辑应允许两种结果。

第四步:权限、协议与安全边界

通知需要用户授予权限,持久通知还需要 Service Worker;相关 API 依赖安全上下文。权限不是业务授权,订单接口仍必须在打开页面后重新鉴权。只允许同源或明确的受信任外部来源,避免开放重定向和钓鱼式深链。

第五步:恢复应用状态与幂等

页面打开后读取路径和 data.orderId,先展示加载状态,再请求订单并校验用户身份。重复点击同一 tag 的通知应合并或更新;客户端路由切换、焦点恢复和埋点都要幂等,避免一次点击创建多个请求或重复扣减状态。

6. 高质量示范回答

我会让 Service Worker 只生成 allowlist 内的同源 navigate URL,并给通知设置稳定 tag。主体点击按浏览器导航,action 若有自己的 URL 则优先使用;没有 URL 的 action 由 notificationclick 聚焦已有窗口或打开回退页。页面打开后重新鉴权、读取订单参数并幂等恢复状态。权限、HTTPS、开放重定向和外部 URL 都单独校验,不能把通知导航当作业务授权。

7. 常见错误

  • 把用户输入直接放入 navigate → 开放重定向 → 只允许同源和路由 allowlist。
  • 认为所有点击都会触发自定义 click 处理 → 主体可能由浏览器直接导航 → 仅对没有 action URL 的分支依赖 notificationclick
  • 把权限当成订单授权 → 越权查看数据 → 页面打开后重新鉴权。
  • 每次点击都 openWindow → 重复窗口和重复请求 → 先匹配同源窗口并让恢复逻辑幂等。
  • 忽略 Service Worker 生命周期 → 异步操作被中断 → 用 event.waitUntil 托管完成的 Promise。

8. 追问及应对

追问一:Notification.navigate 返回什么?

它是只读字符串属性,返回通知导航 URL 的绝对序列化值;没有设置有效 URL 时返回空字串。

追问二:action 没有 navigate 会怎样?

该 action 不会自动导航,点击可进入 notificationclick 事件,由 Service Worker 执行自定义逻辑。

追问三:为什么仍要在页面重新鉴权?

通知 URL 和 data 只是导航提示,不代表用户身份或资源权限。订单 API 必须按当前会话和服务端授权重新检查。

追问四:如何测试跨窗口行为?

覆盖无窗口、已有同源窗口、窗口未受 Service Worker 控制、主体点击、action 点击、权限拒绝和无效 URL,并断言最终只产生一次应用恢复请求。

公开来源

同类题目