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

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

Demián Renzulli
Demián Renzulli

发布时间:2019 年 8 月 19 日

过去,使用多源架构有一些优势。 对于渐进式 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 体验是一项挑战,这主要是因为同源政策会施加许多限制。我们来逐一了解一下。

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 中的网页。

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

缓存

Cache 对象、indexedDB 和 localStorage 也仅限于单个来源。这意味着无法从 https://www.section.example.com 等位置访问属于 https://www.example.com 的缓存。

在上述场景中,您可以采取以下措施来正确管理缓存:

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

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

权限

权限也限定为来源。这意味着,如果用户向来源 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,而无法安装应用的主网址的 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、Cheney Tsai、Martin Schierle 和 Andre Bandarra。