若要设计应用,以充分利用使 PWA 具备可靠性、可安装性和强大功能的技术,首先需要了解您的应用及其限制,并为这两者选择合适的架构。
SPA 与 MPA
如今,Web 开发主要有两种架构模式:单页应用(简称 SPA)和多页应用(简称 MPA)。
单页应用的定义如下:让客户端 JavaScript 根据应用检索或提供给应用的数据,控制网页的大部分或全部 HTML 呈现。应用会替换浏览器的内置导航,并将其替换为其路由和视图处理功能。
多页应用通常会将预渲染的 HTML 直接发送到浏览器,而且通常在浏览器加载完 HTML 后通过客户端 JavaScript 增强该 HTML,并依赖浏览器的内置导航机制来显示后续视图。
这两种架构都可用于创建 PWA。
每种方式都有其优缺点,选择适合您的应用场景和情境的应用是为用户提供快速可靠的体验的关键。
单页应用
- 主要是原子页内更新。
- 启动时加载的客户端依赖项。
- 由于使用了缓存,后续加载的速度会很快。
- 初始加载成本较高。
- 性能取决于设备硬件和网络连接。
- 还需要提高应用复杂性。
在以下情况下,单页应用是很好的架构:
- 用户互动主要围绕同一页面上显示的互连数据(例如实时数据信息中心或视频编辑应用)的原子更新。
- 您的应用具有客户端专用初始化依赖项,例如启动成本极高的第三方身份验证提供程序。
- 视图加载所需的数据取决于特定的客户端专用上下文,例如,显示已连接硬件的控件。
- 应用小而简单,其大小和复杂性不会影响上面列出的缺点。
在以下情况下,SPA 可能不是理想的架构选择:
- 初始加载性能至关重要。SPA 通常需要加载更多 JavaScript,以确定要加载的内容以及如何显示。这种 JavaScript 的解析和执行时间加上检索内容,比发送呈现的 HTML 慢。
- 您的应用主要在低功耗至平均水平的设备上运行。由于 SPA 依赖于 JavaScript 进行呈现,因此与在 MPA 中相比,用户体验依赖于特定设备性能的程度更高。
由于 SPA 需要将浏览器的内置导航替换为其路由,因此 SPA 在高效更新当前视图、管理导航更改以及清理原本将由浏览器处理的旧视图方面需要最低程度的复杂性,这使得它们的总体维护难度增加,也增加了用户设备负担。
多页应用
- 以整页更新为主。
- 初始渲染速度至关重要。
- 客户端脚本是一项增强功能。
- 辅助视图需要另一次服务器调用。
- 上下文不会在视图之间延续。
- 需要服务器或预呈现。
在以下情况下,多页面应用是很好的架构选择:
- 用户互动主要集中在查看单个数据以及可选的基于上下文的数据(例如新闻或电子商务应用)上。
- 初始呈现速度至关重要,因为在加载、解析和执行基于 JavaScript 的替代方案后,将已呈现的 HTML 发送到浏览器比通过数据请求将其组合起来更快。
- 客户端互动或上下文可作为初始加载后的增强功能加入,例如,将配置文件分层到呈现的网页上,或添加与客户端上下文相关的次要组件。
在以下情况下,MPA 可能不是理想的架构选择:
- 重新下载、解析及重新执行 JavaScript 或 CSS 的成本非常高昂。在 PWA 中,使用 Service Worker 可以减轻这种情况。
- 客户端上下文(例如用户位置)无法在视图之间无缝延续,并且重新获取该上下文的成本可能很高。要么需要捕获并检索,要么在两次观看之间重新请求。
因为各个视图需要由服务器动态呈现或在访问前进行预呈现,这可能会限制托管或增加数据的复杂性。
选择哪一个?
尽管有这些优缺点,这两种架构对于创建 PWA 都是有效的。您甚至可以根据应用需求,针对应用的不同部分混合使用它们,例如,让商品详情采用 MPA 架构,结账流程采用 SPA 架构。
无论选择哪种方法,下一步都是了解如何最有效地利用 Service Worker 来提供最佳体验。
Service Worker 的强大功能
除了缓存和网络响应的基本路由和传递之外,Service Worker 还有许多功能。我们可以创建能够改善用户体验和性能的复杂算法。
Service Worker 包括 (SWI)
将 Service Worker 用作网站架构不可或缺的一部分的一种新兴模式是 Service Worker 包含 (SWI)。SWI 会根据其缓存需求将各个资源(通常是 HTML 页面)分成多个部分,然后在 Service Worker 中将它们重新拼接起来,以提高一致性、性能和可靠性,同时缩减缓存大小。
此图片是示例网页。它包含五个不同的部分,将页面细分为:
- 整体布局。
- 全局标题(顶部深色条)。
- 内容区域(左侧中间的线条和图片)。
- 边栏(中间右侧是中等偏深的长条)。
- 页脚(深色底部栏)。
整体布局
整体布局不太可能经常更改,并且没有依赖项。它适合进行预缓存。
页眉和页脚
全局页眉和页脚包含顶部菜单和网站页脚等内容,这带来了特定的挑战:如果页面作为一个整体进行缓存,那么它们可能会在页面加载之间发生变化,具体取决于指定页面的缓存时间。
通过分离这些文件,并将其与内容分开缓存,您可以确保无论缓存何时缓存,用户始终获得相同的版本。由于它们不经常更新,因此也非常适合进行预缓存。不过,它们之间还存在一个依赖关系:网站的 CSS 和 JavaScript。
CSS 和 JavaScript
理想情况下,网站的 CSS 和 JavaScript 应使用过时进行缓存,同时重新验证策略以允许增量更新,而无需更新 Service Worker,如同预缓存资产的情况。然而,每当 Service Worker 使用新的全局页眉或页脚更新时,它们也需要保持在最低版本。因此,在安装 Service Worker 时,其缓存也应使用最新版本的 Asset 进行更新。
内容区域
接下来是内容区域。根据更新的频率,采用网络优先或在重新验证时过时都是合适的策略。正如我们之前讨论的,应使用缓存优先策略缓存图像。
边栏
最后,假设边栏内容包含辅助内容(如标签和相关项),那么从网络中拉取并不重要。这种情况下,在重新验证策略时过时的方法。
现在,了解完所有这些内容后,您可能会想:只能为单页应用执行这种按部分进行缓存的操作。但是,通过在 Service Worker 中采用受边缘包含或服务器端包含启发的模式,您可以通过一些高级 Service Worker 功能为这两种架构实现这一目的。
亲自尝试
您可以在下一个 Codelab 中试用 Service Worker 中包含的功能:
流式响应
在 SPA 环境中,可以使用应用 Shell 模型创建上一个页面,在该模式下,应用 Shell 缓存,然后提供,内容在客户端加载。随着 Streams API 的推出和广泛提供的,应用 Shell 和内容都可以在 Service Worker 中组合并流式传输至浏览器,为您提供应用 Shell 的缓存灵活性,同时享受 MPA 的速度。
其原因在于:
- 视频流可以异步构建,允许不同的视频流片段来自其他来源。
- 在第一个数据块可用后,流的请求者就可以开始处理响应,而不是等待整个数据块完成。
- 针对流式传输进行了优化的解析器(包括浏览器)可以在流式传输完成之前逐步显示数据流的内容,从而加快响应性能的感知性能。
得益于数据流的这三个属性,围绕流式传输构建的架构通常比其他架构具有更快的感知性能。
Streams API 比较复杂且较低级别,因此使用起来可能会比较困难。幸运的是,Workbox 模块可以帮助您为 Service Worker 设置流式响应。
网域、来源和 PWA 范围
Web Worker(包括 Service Worker、存储空间,甚至是已安装的 PWA 的窗口)都受网络最关键的安全机制之一(即同源政策)的约束。在同一来源内,授予权限、共享数据,并且 Service Worker 可以与不同的客户端通信。在同一源外部,系统不会自动授予权限,并且数据是隔离的,无法在不同源之间访问。
同源政策
如果协议、端口和主机相同,则将两个网址定义为具有确切来源。
例如:https://squoosh.app
和 https://squoosh.app/v2
具有相同的源,但 http://squoosh.app
、https://squoosh.com
、https://app.squoosh.app
和 https://squoosh.app:8080
属于不同的源。如需了解详情和示例,请参阅同源政策 MDN 参考文档。
更改子网域并不是主机可以更改的唯一方式。每个主机由一个顶级域名 (TLD)、一个二级域名 (SLD) 以及零个或多个标签(有时称为子域名)组成,中间用点隔开,在网址中从右向左读取。任何项的更改都会导致不同的主机。
在窗口管理模块中,我们已经了解了当用户从已安装的 PWA 导航到其他来源时应用内浏览器的外观。
即使这些网站具有相同的 TLD 和 SLD,但使用了不同的标签,系统仍会显示该应用内浏览器,因为在这种情况下会将它们视为不同的源。
在网络浏览环境中,源站的一个主要方面是存储和权限的工作原理。一个源通过其中的所有内容和 PWA 共享多项功能,包括:
- 存储配额和数据(IndexedDB、Cookie、网络存储空间、缓存存储空间)。
- Service Worker 注册。
- 授予或拒绝的权限(例如 Web 推送、地理定位、传感器)。
- Web 推送注册。
当您从一个源迁移到另一个源时,先前的所有访问权限都会被撤消,因此您必须重新授予权限,而您的 PWA 将无法访问保存在存储设备中的所有数据。