1. 题目与适用场景
电商 Web Push 通知需要把用户带到订单详情。通知可能在应用未打开、已有应用窗口或用户点击某个 action 时触发。请使用 navigate 选项设计深链,并说明 URL 解析、权限、点击回退、重复打开和服务 Worker 生命周期。
2. 面试官考察点
- 是否知道
NotificationOptions.navigate是导航 URL,Notification.navigate只读返回解析后的绝对 URL 或空字串。 - 是否能区分通知主体导航、action 导航和
notificationclick事件回退。 - 是否处理权限、HTTPS、同源策略、开放重定向、重复通知和已有窗口复用。
- 是否能把业务路由恢复、鉴权和幂等放在应用层,而非依赖浏览器自动导航完成。
3. 回答前需要澄清的问题
- 通知由页面脚本还是 Service Worker 的
showNotification创建? - 深链是否允许外部 URL,订单详情需要登录后如何恢复?
- 用户点击通知主体与 action 时,是否要进入不同页面或执行不同动作?
- 已有同源窗口时,产品希望聚焦并复用,还是创建新窗口?
4. 30 秒回答框架
我会在 HTTPS 下由 Service Worker 创建带 navigate 的持久通知,并只写入经过 allowlist 校验的同源 URL。点击主体或 action 时,浏览器可导航到对应 URL;若 action 没有自己的 navigate,在 notificationclick 中处理业务回退,先聚焦已有窗口,再通过客户端路由恢复订单。权限被拒绝、URL 无效或鉴权失败都要有明确的应用层兜底。
5. 分步骤深入解答
第一步:构造经过校验的通知 URL
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 导航
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 与鉴权恢复规则。
第三步:处理点击回退和已有窗口
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 内的同源navigateURL,并给通知设置稳定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,并断言最终只产生一次应用恢复请求。