多源网站中的渐进式 Web 应用

在多源网站中构建渐进式 Web 应用的挑战和解决方法。

德米安·伦祖利
Demián Renzulli

背景

过去,使用多源架构有一些好处,但对于渐进式 Web 应用,该方法带来了许多挑战。具体而言,同源政策对共享 Service Worker 和缓存等内容、权限以及跨多个源实现独立体验施加了限制。本文介绍了多个源的优缺点,并解释了在多源网站中构建渐进式 Web 应用所面临的挑战和解决方法。

多个来源的优缺点

网站采用多源架构有几个合理的理由,主要原因在于提供一组独立的 Web 应用或打造彼此完全隔离的体验。此外,还有一些应避免使用的用途。

良好

我们先来看一下这些有用的原因:

  • 本地化/语言:使用国家/地区代码顶级域名来分隔要在不同国家/地区提供的网站(例如 https://www.google.com.ar),或使用子网域划分定位到不同地理位置的网站(例如:https://newyork.craigslist.org)或以特定语言(例如https://en.wikipedia.org)提供内容。

  • 独立的 Web 应用:使用不同的子网域提供用途与主源网站明显不同的体验。例如,在新闻网站中,填字游戏 Web 应用可能会被 https://crosswords.example.com 特意提供,并作为独立的 PWA 安装和使用,而无需与主网站共享任何资源或功能。

糟糕

如果您没有执行上述任何操作,那么在构建 Progressive Web App 时,使用多源架构很可能是一个劣势。

尽管如此,许多网站仍然以无特定原因或出于“旧版”原因采用这种方式。例如,使用子网域任意分隔网站上本应属于统一体验的部分。

例如,强烈建议您不要使用以下模式:

  • 网站版块:在子网域上分隔网站的不同版块。在新闻网站上,首页通常位于 https://www.example.com,体育版块位于 https://sports.example.com,政治资讯位于 https://politics.example.com,依此类推。对于电子商务网站,使用 https://category.example.com 表示商品类别,使用 https://product.example.com 表示商品页面,等等。

  • 用户流:另一种我们不建议使用的方法是将网站的不同较小部分(例如子网域中的登录页或购买流程页)分开。例如,使用 https://login.example.comhttps://checkout.example.com

对于无法迁移到单源的情形,下面列出了在构建渐进式 Web 应用时可考虑的挑战,以及(如果可能)可考虑的权宜解决方法。

不同来源的 PWA 面临的挑战和临时解决方法

使用多个源构建网站时,提供统一的 PWA 体验极具挑战,这主要是因为同源政策施加了许多限制。下面我们就来逐一了解这些类型。

Service Worker

Service Worker 脚本网址的来源必须与调用 register() 的网页的来源相同。例如,位于 https://www.example.com 的网页无法使用位于 https://section.example.com 的 Service Worker 网址来调用 register()

另一个需要考虑的是,Service Worker 只能控制托管在其来源和路径下的网页。这意味着,如果 Service Worker 托管在 https://www.example.com 上,则它只能控制来自该来源的网址(根据 scope 参数中定义的路径),但不能控制其他子网域(如 https://section.example.com 中的页面)中的任何页面。

在这种情况下,唯一的解决方法是使用多个 Service Worker(每个源一个)。

缓存

Cache 对象、indexedDB 和 localStorage 也限制为单个源。这意味着,无法从 https://www.section.example.com 等服务中访问属于 https://www.example.com 的缓存。

在此类情况下,您可以采取以下措施来正确管理缓存:

  • 利用浏览器缓存:始终建议采用传统浏览器缓存最佳实践。此方法还具有跨源重复使用缓存资源的额外好处,这是使用 Service Worker 的缓存无法实现的。如需了解如何将 HTTP Cache 与 Service Worker 搭配使用,您可以参阅这篇博文

  • 使 Service Worker 的安装保持轻量级:如果您要维护多个 Service Worker,请避免让用户每次导航到新源时都要支付高昂的安装成本。换句话说:仅预缓存绝对必要的资源。

权限

权限范围也仅限于源。也就是说,如果用户向来源 https://section.example.com 授予了指定权限,该权限不会应用到其他来源(例如 https://www.example.com)。

由于无法跨源共享权限,因此此处的唯一解决方案是请求对需要特定功能(例如位置)的每个子网域授予权限。对于网络推送等操作,您可以保留一个 Cookie 来跟踪其他子网域中用户是否已接受权限,以避免再次请求该权限。

安装

如需安装 PWA,每个来源都必须有自己的清单,其中含有相对于自身start_url。这意味着,用户在给定来源(例如:https://section.example.com)上收到安装提示,将无法使用 start_url 在其他来源(例如:https://www.example.com)上安装 PWA。换言之,在子网域中收到安装提示的用户只能为子页面(而不是应用的主网址)安装 PWA。

还存在如下问题:如果每个子网域都符合安装条件,同一用户在浏览网站时可能会收到多个安装提示,并提示用户安装 PWA。

为了缓解此问题,您可以确保仅在主源上显示提示。当用户访问满足安装条件的子网域时:

  1. 监听 beforeinstallprompt 事件
  2. 调用 event.preventDefault()阻止提示显示

这样一来,您就可以确保相应提示不会出现在网站的意外部分,同时您可以继续在主源(例如首页)中显示该提示。

独立模式

在独立窗口中导航时,当用户离开 PWA 清单设置的作用域时,浏览器的行为会有所不同。具体行为取决于各浏览器版本和供应商。例如,当用户在独立模式下移出范围时,最新版本的 Chrome 会打开一个 Chrome 自定义标签页

大多数情况下,没有解决方案,但可以针对托管在子网域中的一小部分体验(例如登录工作流)采用临时解决方法:

  1. 新网址 https://login.example.com 可以在全屏 iframe 中打开。
  2. 在 iframe 内完成任务(例如登录过程)后,您可以使用 postMessage() 将 iframe 中生成的任何信息传回父页面。
  3. 最后,在主网页收到消息后,您可以取消注册监听器,并最终从 DOM 中移除 iframe。

总结

对于基于多个源站构建的网站,如果希望获得一致的 PWA 体验,同源政策会对其施加许多限制。因此,为了向用户提供最佳体验,我们强烈建议您不要将网站分为不同源。

对于已经以这种方式构建的现有网站,使多源 PWA 正常运行可能并非易事,但我们探索了一些可能的权宜解决方法。每种方法都有一定的权衡取舍,因此在决定在您的网站上采用哪一种方法时,请自行判断。

在评估长期策略或网站重新设计时,除非有保留多源架构的重要原因,否则请考虑迁移到单个源。

非常感谢他们的技术评审和建议:Penny Mclachlan、Paul Covell、Dominick Ng、Alberto Medina、Pete LePage、Joe Medley、Cheey Tsai、Martin Schierle 和 Andre Bandarra。