angular service worker搭配workbox使

2018-06-25  本文已影响179人  琢磨先生lf

原文链接
略过前言直接进入主题

什么是 Service Worker?

Service Worker是一个相对较新的技术,用于在应用中运行执行Javascript代码,即使用户还没有打开页面。通过它我们可以在浏览器中注册代码片段、对某些事件进行处理,甚至在我们的例子中,可以对请求进行缓存生成离线应用。

service worker可以设想成浏览器域名下的一个代理,流量通过Service Worker分发,Worker决定数据是从缓存还是从网络获取。缓存是通过service worker API接口定义的,我们可以通过service worker定制自己的缓存策略。

今天我们不打算这样做,事实上,自己实现缓存非常麻烦。幸运的是我们不需要自己造轮子,已经有可用的库提供高级的接口来实现我们需求。本文中我们使用谷歌的Workbox

不是所有的浏览器都支持service worker,但是它们都宣称未来会支持

为什么选择 Workbox?

angular-cli集成了service worker,在angular-cli配置service worker非常容易

新建 angular 项目

ng new angular-service-worker

使用npm i加载依赖

下一步,下载Workbox build API

npm i workbox-build --save-dev

环境以及搭建完成。

为 angular 项目搭建一个 Service Worker 缓存

我们以及下载了Workbox build API,接下来我们就可以去生成angular service worker,为什么我们要生成一个,而不是通过代码配置?

要缓存静态文件,service worker需要知道其文件名,使用 angular-cli 构建项目的时候,目标文件名每次都会改变,angular-cli 在文件名上加了hash值,浏览器可以为新文件作缓存又避免就文件缓存影响

目标文件名每次都会改变,我们无法在 worker-script 去配置,那样每次构建完都要去更改配置,所以我们使用Workbox build API 接口实现。

Workbox build API 允许我们通过 node.js 脚本文件扫描目标文件目录生成 service worker 脚本文件,目标文件大概这样:
dist/sw.js

...
const fileManifest = [
  {
    "url": "index.html",
    "revision": "2a59e6e03216061ad8eebe5d302b63be"
  },
  {
    "url": "inline.5f8f26e0d9e0b566019d.bundle.js",
    "revision": "590f7d098ab30195f9f4761bf79ec18c"
  },
  {
    "url": "main.c0c528c8c7636a1ce8fc.bundle.js",
    "revision": "f1646bf6ca91be91e46d35a173846e5b"
  },
  {
    "url": "polyfills.67d068662b88f84493d2.bundle.js",
    "revision": "ad4076ac41e8c08e5b5f872136081192"
  },
  {
    "url": "styles.d41d8cd98f00b204e980.bundle.css",
    "revision": "d41d8cd98f00b204e9800998ecf8427e"
  },
  {
    "url": "vendor.dd97be8fb65ee0109dba.bundle.js",
    "revision": "dad8873f5866215d28f6a9d07764de3b"
  }
];
workboxSW.precache(fileManifest);
...

缓存静态内容

现在我们开始写脚本文件,我们在项目根目录下新建一个 generate-service-worker.js 文件

const build = require("workbox-build");
const SRC_DIR = "./";
const BUILD_DIR = "dist";

const options = {
  swDest: `${BUILD_DIR}/sw.js`, // target directory
  globDirectory: BUILD_DIR,
  navigateFallback: "/index.html", // redirect all reqest to the fallback, if no explicit route matches
  clientsClaim: true
};

build.generateSW(options).then(() => {
  console.log("Generated service worker with static cache");
});

这段脚本代码中,我们定义了一些变量并将其传递给 builder 的 generateSW 方法,然后 builder 在 BUILD_DIR 指定的目录下创建 service worker 文件

基本就是这样,依靠 angular 生成的 service worker 会缓存所有静态文件。然而,大多数应用程序依赖动态内容,动态内容此时还没被缓存

缓存动态内容

要缓存动态内容,我们需要告诉 Workbox 内容来源,比如说我们有一个接口地址,它提供与应用程序同域下的内容,缓存这类内容,我们需要使用 Workbox builder 的 runtimeCaching 方法,举例说明:
generate-service-worker.js

const options = {
  swDest: `${BUILD_DIR}/sw.js`,
  globDirectory: BUILD_DIR,
  navigateFallback: "/index.html",
  clientsClaim: true,
  runtimeCaching: [
    {
      urlPattern: //api/(.*)/, // reg ex
      handler: "networkFirst" // caching strategy
    }
  ],
  handleFetch: true
};

现在,所有/api/* 下的数据都会被缓存

缓存策略

你还可以定义不同的缓存策略,这是缓存策略

拓展 Service Worker

更多时候,builder 提供的选项不满足我们的应用场景, 比如推送通知缓存到 service worker. 好在service worker 提供用户自定义选项,builder 会通过swSource属性合并两份文件成一个 service worker.
generate-service-worker.js

const options = {
  swSource: `${SRC_DIR}/service-worker.js`,
  swDest: `${BUILD_DIR}/sw.js`,
  globDirectory: BUILD_DIR,
  navigateFallback: "/index.html",
  clientsClaim: true,
  runtimeCaching: [
    {
      urlPattern: //api/(.*)/, // reg ex
      handler: "networkFirst" // caching strategy
    }
  ],
  handleFetch: true
};

同时我们将方法名从generateSW() 改为 injectManifest()
generate-service-worker.js

build.injectManifest(options).then(() => {
    
});

为 Service Worker 配置构建脚本

到这一步,使用 Workbox 不会比 使用 angular-cli service worker 复杂

要想在构建时自动生成 generate-service-worker.js ,我们需要手动在构建脚本关联 service worker。

有一个办法是定制angular-cli 使用 Workbox Webpack pluign,这个可以让 generate-service-worker.js 运行良好,但会破坏angular-cli结构

我们可以在运行 "npm start" 启动项目的时候,通过 express 运行 node.js 脚本,这两个都不算是最佳方案,如果你更好的方案,请联系我
这是 npm package.json

...
"scripts": {
    "ng": "ng",
    "start": "ng build --prod && npm run gn-sw && node server/server.js",
    "build": "ng build --prod && npm run gn-sw",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "gn-sw": "node generate-service-worker.js"
  },
...

const express = require("express");
const app = express();

app.use(express.static(__dirname + "/../dist/"));

app.listen(process.env.PORT || 8080, () => {});
上一篇下一篇

猜你喜欢

热点阅读