0
点赞
收藏
分享

微信扫一扫

#yyds干货盘点#使用 VitePWA 插件使站点离线工作

​​Anthony Fu​​​​的 VitePWA 插件​​​是您的 Vite 网站的绝佳工具。它可以帮助您添加一个服务工作者来处理:离线支持缓存资产和内容当有新内容可用时提示用户……还有其他好东西!我们将一起了解服务工作者的概念,然后直接使用 VitePWA 插件制作一个。刚接触维特?查看​​我之前的帖子​​以获取介绍。

目录

  1. ​​服务人员,介绍​​
  2. ​​版本控制和清单​​
  3. ​​我们的第一个服务人员​​
  4. ​​离线功能呢?​​
  5. ​​服务人员如何更新​​
  6. ​​更新内容的更好方法​​
  7. ​​运行时缓存​​
  8. ​​添加您自己的 Service Worker 内容​​
  9. ​​包起来​​

服务人员,介绍

在进入 VitePWA 插件之前,让我们简单谈谈​​Service Worker​​本身。

Service Worker是一个后台进程,在您的 Web 应用程序的单独线程上运行。服务工作者有能力拦截网络请求并做……任何事情。可能性惊人地广泛。例如,您可以拦截对 TypeScript 文件的请求并即时编译它们。或者您可以拦截对视频文件的请求并执行浏览器当前不支持的高级转码。更常见的是,服务工作者用于缓存资产,既可以提高站点的性能,也可以使其在离线时执行​某些操作。

当有人第一次登陆您的网站时,VitePWA 插件创建的服务工作者会利用​​缓存存储 API​​安装并缓存您的所有 HTML、CSS 和 JavaScript 文件。结果是,在后续访问您的站点时,浏览器将从缓存中加载这些资源,而不需要发出网络请求。甚至在第一次访问您的站点时,由于 service worker​只是​预先缓存了所有内容,因此您的用户单击的下一个位置可能已经预先缓存,从而允许浏览器完全绕过网络请求。

版本控制和清单

您可能想知道当您的代码更新时,Service Worker 会发生什么。如果您的服务工作人员正在缓存一个​​foo.js​​文件,并且您修改了该文件,您希望服务工作人员在下次用户访问该站点时下拉更新版本。

但实际上你没有​​foo.js​​​文件。通常,构建系统会创建类似 的内容​​foo-ABC123.js​​​,其中“ABC123”是文件的哈希值。如果您更新​​foo.js​​​,您的站点的下一次部署可能会发送过来​​foo-XYZ987.js​​。服务人员如何处理这个问题?

事实证明,Service Worker API 是一个​极低​级的原语。如果您正在寻找它和缓存 API 之间的原生交钥匙解决方案,您会感到失望。​基本上,服务工作者的创建需要部分自动化,并连接到构建系统。​您需要查看您的构建创建的所有资产,将这些文件名硬编码到服务工作者中,有代码来预缓存它们,更重要的是,​跟踪​缓存的文件。

如果代码更新,服务工作者文件也会更改,包含​​的文件名,并带有哈希值。当用户下次访问应用程序时,新的 Service Worker 将需要安装新的文件清单,并将新文件清单与当前缓存中的清单进行比较,弹出不再需要的文件,同时缓存新内容。

这是一项荒谬的工作,而且很难做到正确。虽然它可能是一个有趣的项目,但在实践中,您会希望使用已建立的产品来生成您的服务工作者——而最好的产品是来自 Google 的​​Workbox​​。

甚至 Workbox 也有点低级原语。它需要有关您预缓存的文件的详细信息,这些文件隐藏在您的构建工具中。这就是我们使用 VitePWA 插件的原因。它在后台使用 Workbox,并使用 Vite 创建的捆绑包所需的所有信息对其进行配置。不出所料,如果您碰巧更喜欢使用这些捆绑器,那么还有​​webpack​​​和​​Rollup插件。​​

我们的第一个服务人员

我假设你已经有一个基于 Vite 的网站。如果没有,请随意从任何​​可用模板中​​​​创建一个​​。

首先,我们安装 VitePWA 插件:

npm i vite-plugin-pwa

我们将在 Vite 配置中导入插件:

import { VitePWA } from "vite-plugin-pwa"

然后我们也把它用在配置中:

plugins: [
VitePWA()

我们稍后会添加更多选项,但这就是我们创建一个非常有用的服务工作者所需要的全部内容。现在让我们使用以下代码在应用程序条目中的某处注册它:

import { registerSW } from "virtual:pwa-register";

if ("serviceWorker" in navigator) {
// && !/localhost/.test(window.location)) {
registerSW();
}

不要让被注释掉的代码让你陷入循环。事实上,这非常重要,因为它会阻止服务工作者在开发中运行。我们只想在我们正在开发的本地主机上以外的任何地方安装服务工作者,也就是说,除非我们正在开发服务工作者本身,在这种情况下,我们可以注释掉该检查(并在将代码推送到主枝)。

让我们继续打开一个新的浏览器,启动 DevTools,导航到 Network 选项卡,然后运行 Web 应用程序。一切都应该像您通常期望的那样加载。不同之处在于您应该在 DevTools 中看到大量的网络请求。

那是 Workbox 预缓存捆绑包。一切正常!

离线功能呢?

因此,我们的服务人员正在预先缓存我们所有的捆绑资产。这意味着它将从缓存中为这些资产提供服务,甚至无需访问网络。这是否意味着即使用户没有网络访问权限,我们的 service worker 也可以为资产提供服务?确实如此!

而且,不管你信不信,它已经完成了。通过在 DevTools 中打开 Network 选项卡并告诉 Chrome 模拟离线模式来尝试一下,就像这样。

“无节流”选项是默认选择。单击它并选择“离线”选项以模拟离线连接。

让我们刷新一下页面。你​应该​看到一切都加载了。当然,如果您正在运行任何网络请求,您会看到它们永远挂起,因为您处于离线状态。但是,即使在这里,您也可以做一些事情。现代浏览器带有自己的内部持久数据库,称为 IndexedDB。没有什么可以阻止您编写自己的代码以将一些数据同步到那里,然后编写一些自定义服务工作者代码来拦截网络请求,确定用户是否离线,然后从 IndexedDB 提供等效的内容(如果它在那里)。

但更简单的选择是检测​​用户是否离线​​​,显示关于离线的消息,然后绕过数据请求。这本身就是一个话题,​​我已经写得​​更详细了。

在向您展示如何编写和集成您自己的 Service Worker 内容之前,让我们仔细看看我们现有的 Service Worker。特别是,让我们看看它如何管理更新/更改内容。即使使用 VitePWA 插件,这也非常棘手且容易搞砸。

在继续之前,请确保您告诉 Chrome DevTools 让您重新上线。

服务人员如何更新

仔细看看当我们更改内容时我们的网站会发生什么。我们将继续删除现有的服务工作者,我们可以在 DevTools 的 Application 选项卡中的 Storage 下执行此操作。

#yyds干货盘点#使用 VitePWA 插件使站点离线工作_缓存

单击“清除站点数据”按钮以获得一个干净的状态。在此期间,我将删除我自己站点的大部分路由,以减少资源,然后让 Vite 重建应用程序。

查看生成的​​sw.js​​以查看生成的 Workbox 服务工作者。里面应该有一个预缓存清单。我的看起来像这样:

#yyds干货盘点#使用 VitePWA 插件使站点离线工作_缓存_02

如果​​sw.js​​是缩小的,请通过 Prettier 运行它以使其更易于阅读。

现在让我们运行该站点并查看缓存中的内容:

#yyds干货盘点#使用 VitePWA 插件使站点离线工作_应用程序_03

让我们专注于​​settings.js file​​​. Vite​​assets/settings.ccb080c2.js​​基于其内容的哈希生成。独立于 Vite 的 Workbox 生成​了自己​的同一文件的哈希。如果要生成具有不同内容的相同文件名,则将使用不同的预缓存清单(相同的文件,但不同的修订版)重新生成新的服务工作者,并且 Workbox 将知道缓存新版本,并且不再需要时删除旧的。

同样,文件名总是不同的,因为我们使用了一个将哈希码注入文件名的捆绑程序,但 Workbox 支持不这样做的开发环境。

自撰写本文以来,VitePWA 插件已更新,不再注入这些修订哈希。如果您尝试按照本文中的步骤进行操作,则此特定步骤可能与您的实际体验略有不同。有关更多上下文,请参阅​​此 GitHub 问题​​。

如果我们更新我们的​​settings.js​​文件,那么 Vite 将在我们的构建中创建一个带有新哈希码的新文件,Workbox 会将其视为一个新文件。让我们看看这个在行动。更改文件并重新运行 Vite 构建后,我们的预缓存清单如下所示:

现在,当我们刷新页面时,先前的 service worker​仍在​运行并加载​先前​的文件。然后,下载并预缓存具有​​预缓存清单的​​服务工作者。​

新的预缓存清单显示在缓存资产列表中。请注意,我们的设置文件的两个版本都存在(并且其他一些资产的两个版本也受到了影响):旧版本,因为它仍在运行,而新版本,因为新的服务工作者已经预先缓存它。

请注意这里的推论:我们的旧内容仍在提供给用户,因为旧的服务工作者仍在运行。用户无法看到我们刚刚所做的更改,即使他们刷新也是如此,因为服务工作者默认情况下会保证此 Web 应用程序的所有选项卡都运行​相同​的版本。如果您希望浏览器显示更新版本,请关闭您的选项卡(以及该站点的任何其他选项卡),然后重新打开它。

缓存现在应该只包含新资产。

Workbox 做了所有让这一切正确的工作!我们做的很少。

更新内容的更好方法

在用户碰巧关闭所有浏览器选项卡之前,您不太可能向用户提供陈旧的内容。幸运的是,VitePWA 插件提供了更好的方法。该​​registerSW函数​​​接受带有​​onNeedRefresh​​​方法的对象。只要有新的服务工作者等待接管,就会调用此方法。​​registerSW​​还返回一个函数,您可以调用该函数来重新加载页面,在此过程中激活新的服务工作者。

很多,所以让我们看一些代码:

if ("serviceWorker" in navigator) {
// && !/localhost/.test(window.location) && !/lvh.me/.test(window.location)) {
const updateSW = registerSW({
onNeedRefresh() {
Toastify({
text: `<h4 style='display: inline'>An update is available!</h4>
<br><br>
<a class='do-sw-update'>Click to update and reload</a> `,
escapeMarkup: false,
gravity: "bottom",
onClick() {
updateSW(true);
}
}).showToast();
}
});
}

我正在使用​​toastify-js 库​​来显示一个 toast UI 组件,让用户知道何时有新版本的 service worker 可用和等待。如果用户单击 toast,我会调用 VitePWA 提供的函数来重新加载页面,同时运行新的 service worker。

现在,当我们有待处理的更新时,前端会弹出一个不错的 toast 组件。单击它会重新加载包含新内容的页面。

这里要记住的一件事是,在您部署代码以显示 toast 后,下次加载站点时将不会显示 toast 组件。那是因为旧的 service worker(我们添加 toast 组件之前的那个)仍在运行。这需要手动关闭所有选项卡并重新打开 Web 应用程序以供新的服务人员接管。然后,​下次​你更新一些代码时,service worker 应该显示 toast,提示你更新。

为什么刷新页面时 service worker 不更新?我之前提到刷新页面不会更新或激活等待的 Service Worker,那么为什么会​这样​​呢?调用这个方法不仅会刷新页面,还会调用一些低级的 Service Worker API(特别是​​skipWaiting​​),从而得到我们想要的结果。

运行时缓存

我们已经看到了使用 VitePWA 免费获得的用于构建资产的捆绑包预缓存。缓存我们在运行时可能请求的任何其他内容怎么样?Workbox 通过其​​runtimeCaching​​功能支持这一点。

就是这样。VitePWA 插件可以接受一个对象,其中一个属性是​​workbox​​,它接受 Workbox 属性。

const getCache = ({ name, pattern }: any) => ({
urlPattern: pattern,
handler: "CacheFirst" as const,
options: {
cacheName: name,
expiration: {
maxEntries: 500,
maxAgeSeconds: 60 * 60 * 24 * 365 * 2 // 2 years
},
cacheableResponse: {
statuses: [200]
}
}
});
// ...

plugins: [
VitePWA({
workbox: {
runtimeCaching: [
getCache({
pattern: /^https:\/\/s3.amazonaws.com\/my-library-cover-uploads/,
name: "local-images1"
}),
getCache({
pattern: /^https:\/\/my-library-cover-uploads.s3.amazonaws.com/,
name: "local-images2"
})
]
}
})
],
// ...

我知道,这是很多代码。但它真正做的只是告诉 Workbox 缓存它看到的与这些 URL 模式匹配的任何内容。如果您想深入了解细节​​,文档会提供更多信息。​​

现在,在更新生效后,我们可以看到我们的 service worker 正在为这些资源提供服务。

我们可以看到创建的相应缓存。

#yyds干货盘点#使用 VitePWA 插件使站点离线工作_离线_04

添加您自己的 Service Worker 内容

假设您想与服务人员一起进步。您想添加一些代码来与 IndexedDB 同步数据,添加 fetch 处理程序,并在用户离线时使用 IndexedDB 数据进行响应(同样,​​我之前的帖子​​介绍了 IndexedDB 的来龙去脉)。但是如何将自己的代码放入 Vite 为我们创建的 service worker 中呢?

我们可以为此使用另一个 Workbox 选项:​​importScripts​​.

VitePWA({
workbox: {
importScripts: ["sw-code.js"],

在这里,服务工作者将​​sw-code.js​​​在运行时请求。在这种情况下,请确保​​sw-code.js​​​您的应用程序可以提供文件。实现这一点的最简单方法是将其放在​​public​​​文件夹中(有关详细说明,请参阅​​Vite 文档​​)。

如果此文件开始增长到需要使用 JavaScript 导入来分解的大小,请确保将其捆绑以防止服务工作者尝试执行导入语句(它可能会也可能不会执行)。你可以创建一个单独的 Vite 版本。

包起来

在 2021 年底,CSS-Tricks 询问了一群前端人员,他们可以做些什么来让他们的网站变得更好。​​Chris Ferdinandi 建议使用服务人员。​​嗯,这正是我们在本文中完成的,而且相对简单,不是吗?这要归功于 VitePWA 以及 Workbox 和 Cache API 的帽子提示。

利用 Cache API 的服务工作者能够极大地提高您的 Web 应用程序的性能。虽然一开始可能看起来有点可怕或令人困惑,但很高兴知道我们有像 VitePWA 插件这样的工具可以大大简化事情。安装插件并让它完成繁重的工作。当然,服务工作者可以做更多高级的事情,VitePWA 可以用于更复杂的功能,但离线站点是一个很棒的起点!


举报

相关推荐

0 条评论