让你的 Web 应用离线工作,第一部分:设置

Avatar of Adam Rackis
Adam Rackis

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

这个两部分的系列文章是对离线 Web 开发的简单介绍。 使 Web 应用程序在离线状态下执行某些操作非常棘手,需要很多东西到位并正常运行。 我们将从高层次概述所有这些部分,并提供工作示例。 这篇文章是一个概述,但整个过程中列出了许多更详细的资源。

文章系列

  1. 设置(您当前位置)
  2. 实现

基本方法

我将大量使用 JavaScript 的 async/await 语法。 它在所有主要浏览器和 Node 中都受支持,并且极大地简化了基于 Promise 的代码。 上面的链接很好地解释了 async,但简而言之,它们允许您解析 Promise,并使用 await 直接在代码中访问其值,而不是调用 .then 并在回调中访问值,这通常会导致可怕的“向右漂移”。

我们正在构建什么?

我们将扩展现有的 booklist 项目,以将当前用户的书籍同步到 IndexedDB,并创建一个简化的离线页面,即使用户没有网络连接也能显示。

从 Service Worker 开始

离线开发所需的唯一不可协商的事情是 Service Worker。 Service Worker 是一个后台进程,除了其他功能外,还可以拦截网络请求;重定向它们;通过返回缓存的响应来短路它们;或者像往常一样执行它们并对响应执行自定义操作,例如缓存。

基本缓存

您使用 Service Worker 执行的第一项、最基本但影响最大的操作可能是缓存应用程序的资源。 Service Worker 及其使用的缓存是极其底层的原语;一切都必须手动操作。 为了正确缓存您的资源,您需要获取并将它们添加到缓存中,但随后您还需要跟踪这些资源的变化。 您需要跟踪它们何时更改,删除先前版本,然后获取并更新新版本。

在实践中,这意味着您的 Service Worker 代码需要作为构建步骤的一部分生成,该步骤对您的文件进行哈希处理,并生成一个足够智能的文件来记录这些版本之间的更改,并在需要时更新缓存。

抽象来救援

这是极其繁琐且容易出错的代码,您可能永远不想自己编写它。 幸运的是,一些聪明人编写了抽象来提供帮助,即 Google 的优秀人员编写的 sw-precachesw-toolbox。 请注意,Google 后来弃用了这些工具,转而支持更新的 Workbox。 我还没有迁移我的代码,因为 sw-* 运行良好,但无论如何,想法都是一样的,而且我听说转换很容易。 值得一提的是,sw-precache 目前每天大约有 30,000 次下载,因此它仍然被广泛使用。

你好世界,sw-precache

让我们直接开始。 我们正在使用 webpack,就像 webpack 一样,有一个插件,所以让我们先看看它。

// inside your webpack config
new SWPrecacheWebpackPlugin({
  mergeStaticsConfig: true,
  filename: "service-worker.js",
  staticFileGlobs: [ //static resources to cache
    "static/bootstrap/css/bootstrap-booklist-build.css",
    ...
  ],
  ignoreUrlParametersMatching: /./,
  stripPrefixMulti: { //any paths that need adjusting
    "static/": "react-redux/static/", 
    ...
  },
  ...
})

默认情况下,webpack 生成的所有捆绑包都将被预缓存。 我们还在 staticFileGlobs 属性中手动提供了一些我想缓存的静态资源的路径,并且我正在调整 stripPrefixMulti 中的一些路径。

// inside your webpack config
const getCache = ({ name, pattern, expires, maxEntries }) => ({
  urlPattern: pattern,
  handler: "cacheFirst",
  options: {
    cache: {
      maxEntries: maxEntries || 500,
      name: name,
      maxAgeSeconds: expires || 60 * 60 * 24 * 365 * 2 //2 years
    },
    successResponses: /0|[123].*/
  }
});

new SWPrecacheWebpackPlugin({
  ...
  runtimeCaching: [ //pulls in sw-toolbox and caches dynamically based on a pattern
    getCache({ pattern: /^https:\/\/images-na.ssl-images-amazon.com/, name: "amazon-images1" }),
    getCache({ pattern: /book\/searchBooks/, name: "book-search", expires: 60 * 7 }), //7 minutes
    ...
  ]
})

runtimeCaching 部分添加到我们的 SWPrecacheWebpackPlugin 中会引入 sw-toolbox,并允许我们根据需要动态缓存与特定模式匹配的 URL——getCache 有助于将样板代码保持在最低限度。

你好世界,sw-toolbox

生成的整个 Service Worker 文件非常大,但让我们只看一小部分,即上面动态缓存之一。

toolbox.router.get(/^https:\/\/images-na.ssl-images-amazon.com/, toolbox.cacheFirst, {
  cache: { maxEntries: 500, name: "amazon-images1", maxAgeSeconds: 63072000 },
  successResponses: /0|[123].*/
});

sw-toolbox 为我们提供了一个很好的高级路由器对象,我们可以使用它以 MVC 样式挂接到各种 URL 请求。 我们将使用它来很快设置离线功能。

不要忘记注册 Service Worker

当然,上面生成的 Service Worker 文件本身的存在没有任何用处;它需要注册。 代码如下所示,但请确保将其放在 onload 监听器或其他保证在页面加载后运行的地方。

if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/service-worker.js");
}

就是这样! 我们让一个基本的 Service Worker 运行起来,它缓存了我们的应用程序资源。 请继续关注明天,我们将扩展它以支持离线功能。

文章系列

  1. 设置(您当前位置)
  2. 实现