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

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

背景

过去,使用多源架构有一些优势,但对于渐进式 Web 应用而言,这种方法带来了许多挑战。特别是同源策略,对共享诸如服务工作线程和缓存、权限以及跨多个源实现独立体验等内容施加了限制。本文将介绍多源的优缺点,并解释在多源站点中构建渐进式 Web 应用的挑战和解决方法。

多源的优缺点

站点采用多源架构有几个合理的理由,主要与提供独立的 Web 应用程序集或创建彼此完全隔离的体验有关。也有一些应该避免的用途。

优点

让我们先看看有用的理由

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

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

缺点

如果您没有进行任何这些操作,那么在构建渐进式 Web 应用时,使用多源架构可能会成为一种劣势。

尽管如此,许多站点仍然以这种方式构建,没有特别的原因,或者因为“遗留”原因。一个例子是使用子域名来任意分隔应该属于统一体验的站点的各个部分。

例如,以下模式是强烈不推荐的

  • 站点部分: 在子域名上分隔站点的不同部分。在新闻站点中,通常会看到主页位于: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 体验具有挑战性,主要是因为同源策略,它施加了许多约束。让我们一次查看一个。

服务工作线程

服务工作线程脚本 URL 的源必须与调用 register() 的页面的源相同。这意味着,例如,位于 https://www.example.com 的页面无法使用位于 https://section.example.com 的服务工作线程 URL 调用 register()

另一个需要考虑的因素是,服务工作线程只能控制托管在其所属的源和路径下的页面。这意味着,如果服务工作线程托管在 https://www.example.com,则它只能控制来自该源的 URL(根据 scope 参数中定义的路径),但不会控制其他子域名中的任何页面,例如 https://section.example.com 中的页面。

在这种情况下,唯一的解决方法是使用多个服务工作线程(每个源一个)。

缓存

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

以下是在这种情况下正确管理缓存的一些方法

  • 利用浏览器缓存: 始终建议使用 传统的浏览器缓存最佳实践。此技术提供了跨源重用缓存资源的额外好处,这是服务工作线程的缓存无法实现的。有关如何将 HTTP 缓存与服务工作线程一起使用的最佳实践,您可以查看这篇文章

  • 保持服务工作线程安装轻量化: 如果您维护多个服务工作线程,请避免用户每次导航到新源时都付出高昂的安装成本。换句话说:仅预缓存绝对必要的资源。

权限

权限也限定在源范围内。这意味着,如果用户向源 https://section.example.com 授予了给定的权限,则该权限不会延续到其他源,例如 https://www.example.com

由于无法跨源共享权限,因此唯一的解决方案是在需要给定功能的每个子域名上请求权限(例如,位置信息)。对于 Web 推送等功能,您可以维护一个 Cookie 来跟踪用户是否在另一个子域名中接受了权限,以避免再次请求。

安装

要安装 PWA,每个源都必须有自己的清单,其中 start_url 相对于自身。这意味着,在给定源(即:https://section.example.com)上收到安装提示的用户将无法使用不同源(即:https://www.example.com)上的 start_url 安装 PWA。换句话说,在子域名中收到安装提示的用户将只能安装子页面的 PWA,而不能安装应用的 main URL。

还有一个问题是,如果每个子域名都满足安装条件,并提示用户安装 PWA,则同一用户在浏览站点时可能会收到多个安装提示。

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

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

这样,您可以确保提示不会显示在站点的意外部分,同时您可以继续在主源(例如,主页)中显示提示。

独立模式

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

在大多数情况下,没有针对此问题的解决方案,但可以为托管在子域名中的小部分体验(例如:登录工作流程)应用解决方法

  1. 新的 URL 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、Cheney Tsai、Martin Schierle 和 Andre Bandarra 的技术审核和建议。