FCM Web Notification安卓端点击仅聚焦最后标签页无法触发openWindow问题
问题根因
- 安卓Chrome后台休眠后会严格限制Service Worker的执行时长,你当前代码把
clients.openWindow放在clients.matchAll的异步回调中执行,后台状态下matchAll查询所有标签页的耗时过长,还没执行到openWindow逻辑,Service Worker就被系统终止了,此时Chrome会触发默认行为:聚焦最后一次活跃的浏览器标签页。 - 你开启了
includeUncontrolled: true参数,会让matchAll查询所有不受当前Service Worker控制的标签页,进一步拉长查询耗时,提升了超时被终止的概率。 - 安卓Chrome要求
openWindow必须在用户交互(点击通知属于用户交互)触发后的极短时间内调用,放在异步回调中的延迟调用会被浏览器的弹窗拦截规则判定为非用户主动触发,直接拦截,最终走浏览器默认聚焦逻辑。
修复方案
方案1(不需要复用已有标签页场景)
如果你本来就不需要聚焦已打开的对应域名标签,直接删除冗余的matchAll逻辑,直接调用openWindow即可:
self.addEventListener('notificationclick', function (event) { const url = event.notification.data; console.log('On notification click: ', event); event.notification.close(); if (clients.openWindow) { event.waitUntil(clients.openWindow(url)); } });
方案2(需要复用已有标签页场景)
如果确实需要先检查是否有已打开的对应URL标签,优化查询参数并增加超时兜底,避免异步逻辑超时被终止:
self.addEventListener('notificationclick', function (event) { const url = event.notification.data; event.notification.close(); // 兜底逻辑:超时1秒未完成查询直接打开新标签 const openNew = () => clients.openWindow ? clients.openWindow(url) : Promise.resolve(); const checkPromise = clients.matchAll({ type: 'window' }).then(windowClients => { for (const client of windowClients) { if (client.url === url && 'focus' in client) { return client.focus(); } } return openNew(); }); // 用Promise.race避免后台执行超时 event.waitUntil(Promise.race([ checkPromise, new Promise(resolve => setTimeout(() => resolve(openNew()), 1000)) ])); });
额外注意事项
- 确保
event.notification.data返回的是完整的绝对URL,不要使用相对路径,安卓后台状态下相对路径解析失败也会导致openWindow触发失败。 - 不要在
notificationclick回调中执行多余的日志、cookie读写等操作,额外逻辑会占用Service Worker的执行时间,增大被系统终止的概率。 - 确认设备给Chrome开启了后台弹出页面的权限,部分国产ROM会默认禁用该权限,也会导致
openWindow调用失败。
内容的提问来源于stack exchange,提问作者Luca Mangiacotti
相关产品推荐
相关产品推荐

