如何实现iframe内嵌外部网站的通知在主PWA应用中显示?
嗨,我看你这个问题确实戳中了跨域iframe通知的痛点——毕竟外部服务不在自己控制范围内,还要把它的通知拿到主PWA里显示,确实有点棘手。我来给你捋几个实际能用的思路,还有说说你之前尝试的方法为啥没起效~
先说说你之前尝试的方法的问题
你写的那个Service Worker监听push事件的代码,其实从根源上就行不通哦。因为浏览器的Push通知是和域名强绑定的:外部消息服务的Push通知只会推送给它自己域名下注册的Service Worker,你的主PWA的Service Worker根本收不到这些Push事件。另外你手动设置的通知权限,主PWA的权限和iframe的权限是完全独立的——就算给主PWA开了权限,iframe自己的域名还是需要单独授权,而且就算授权了,iframe的通知会显示它自己的域名,体验也不如主PWA直接显示的好。
可行的解决方案思路
思路1:让外部服务商配合用postMessage传递通知(最优解)
如果能联系到外部消息服务的开发团队,让他们做一点小改动,这是最顺畅的方案:
- 首先在你的主PWA页面里,添加一个
message事件监听,专门过滤目标外部服务发来的通知内容:// 主PWA页面的代码 window.addEventListener('message', (event) => { // 只接收目标外部服务域名的消息,防止恶意注入 if (event.origin !== 'https://app.schul.cloud') return; // 确认是通知类型的消息 if (event.data?.type === 'notification') { // 用主PWA的权限显示通知 if (Notification.permission === 'granted') { new Notification('来自消息服务的通知', { body: event.data.body, icon: 'icon.png', // 可以加更多自定义配置,比如badge、数据字段等 }); } } }); - 然后让外部服务商在他们的消息服务里,当有新通知触发时,调用
window.parent.postMessage把通知内容发出来就行。
不过这个方案的前提是服务商愿意配合,毕竟你没法直接修改他们的代码。
思路2:轮询外部消息服务的API(无服务商配合时的备选)
如果外部消息服务提供了查询未读通知/新消息的API(比如REST接口),那主PWA可以通过Service Worker定时轮询这个接口,拉到新通知后自己显示:
- 在你的Service Worker里写个定时任务就行,用
Background Sync会更优雅,但用setInterval也能快速实现:
这个方案的关键是要确认两个点:一是外部服务的API允许你的主PWA域名跨域请求(支持CORS),二是你能带上用户的认证信息,确保拉取的是当前用户的专属通知。// Service Worker代码 let pollTimer; self.addEventListener('activate', (event) => { // 激活Service Worker后启动轮询,比如每5分钟查一次 pollTimer = setInterval(async () => { try { // 请求外部服务的通知API,记得带上用户的认证信息(比如Cookie或Token) const response = await fetch('https://app.schul.cloud/api/notifications', { credentials: 'include', headers: { 'Content-Type': 'application/json' } }); const notifications = await response.json(); // 过滤出未读的新通知 const unreadNotifies = notifications.filter(notify => !notify.isRead); if (unreadNotifies.length > 0) { unreadNotifies.forEach(notify => { self.registration.showNotification('来自消息服务的通知', { body: notify.content, icon: 'icon.png' }); }); } } catch (err) { console.error('轮询通知失败:', err); } }, 300000); // 5分钟,可根据需求调整间隔 }); // 停止Service Worker时清除定时器 self.addEventListener('deactivate', () => { clearInterval(pollTimer); });
为啥其他“直接捕获iframe通知”的思路行不通?
比如你可能想过直接拦截iframe的Notification构造函数,但因为同源策略的限制,主PWA根本没法访问跨域iframe的window对象,更别说重写它的API了,所以这类方法基本都走不通。
总结一下
如果能联系到外部服务商配合用postMessage,这是体验最好的方案;如果不行,就优先看看有没有可轮询的API;其他绕开同源策略的野路子要么不可靠,要么违反浏览器的安全规则,不建议尝试。
备注:内容来源于stack exchange,提问作者dclausen

