webpack构建离线应用 Service Workers

本项目目录,承接上一篇博客:webpack搭建多页面应用:https://blog.csdn.net/qq_41831345/article/details/104390551

1 简介

Service Workers是一个在浏览器后台运行的脚本,它的生命周期完 全独立于网页。它无法直接访问DOM,但可以通过postMessage接口发 送消息来和UI进程通信。拦截网络请求是Service Workers的重要功能, 通过Service Workers能完成离线缓存、编辑响应、过滤响应等功能。

2 兼容性

判断浏览器是否支持Service Workers的最简单方法是通过以下代 码:

if (navigator.serviceWorker) {
 // 可以使用navigator.serviceWorker
}

3 注册 

要为网页接入Service Workers,就需要在网页加载后注册一个描述 Service Workers逻辑的脚本。代码如下: 

<script>
    if (navigator.serviceWorker) {
        window.addEventListener('load', () => {
            navigator.serviceWorker.register('../sw.js').then((registration) => {
                // Registration was successful
                console.log('ServiceWorker registration successful with scope: ', registration.scope);
            }, (err) => {
                // registration failed :(
                console.log('ServiceWorker registration failed: ', err);
            });
        });
    }
</script>

一旦这个脚本文件被加载,Service Workers的安装就开始了。在这 个脚本被安装到浏览器中后,就算用户关闭了当前网页,它仍会存在。 也就是说第一次打开该网页时,Service Workers的逻辑不会生效,因为 脚本还没有被加载和注册,但是以后再次打开该网页时脚本里的逻辑将 会生效。

在Chrome中可以通过打开网址chrome://inspect/#service-workers 来查看当前浏览器中所有已注册的Service Workers。

4 使用 Service Workers实现离线缓存

Service Workers 在注册成功后会在其生命周期中派发一些事件,通过监听对应的事件在特点的时间节点上做一些事情。在Service Workers脚本中引入了新的关键字self,代表当前的Service Workers实例。

项目根目录下新建一个sw.js文件。

在Service Workers安装成功后会派发出install事件,需要在这个事件 中执行缓存资源的逻辑,实现代码如下:

// 当前缓存版本的唯一标示,以时间为基础
const cacheKey = new Date().toISOString();

// 需要被缓存的文件的url列别
const cacheList = [
  '/',
  '/dist/main.css',
  '/dist/main.js',
];
// 监听事件
// eslint-disable-next-line no-restricted-globals
self.addEventListener('install', (event) => {
  // Perform install steps
  event.waitUntil(
    caches.open(cacheKey)
      .then((cache) => {
        console.log('Opened cache');
        return cache.addAll(cacheList);
      }),
  );
});
接下来需要监听网络请求事件去拦截请求、复用缓存,代码如下:

// 拦截网络请求
// eslint-disable-next-line no-restricted-globals
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => {
        // Cache hit - return response 本地命中缓存,返回缓存的值
        if (response) {
          return response;
        }
        // 否则就用fetch下载资源
        return fetch(event.request);
      }),
  );
});

5 代码更新

线上的代码有时需要更新和重新发布,如果这个文件被离线缓存 了,就需要在 Service Workers脚本中有对应的逻辑去更新缓存。这可以 通过更新Service Workers脚本文件做到。

浏览器针对Service Workers有如下机制。

· 每次打开接入了Service Workers的网页时,浏览器都会重新下载 Service Workers脚本文件(所以要注意该脚本文件不能太大)。如果发 现和当前已经注册过的文件存在字节差异,就将其视为“新服务工作线 程”。

· 新的Service Workers线程将会启动,且将会触发其install事件。
· 当网站上当前打开的页面关闭时,旧的 Service Workers 线程将会被终止,新的Service Workers线程将会取得控制权。
· 新的Service Workers线程取得控制权后,将会触发其activate事件。

新的Service Workers线程中的activate事件就是清理旧缓存的最佳时 间点,代码如下:

/ 代码的更新
// eslint-disable-next-line no-restricted-globals
self.addEventListener('activate', (event) => {
  const cacheWhitelist = [cacheKey];

  event.waitUntil(
    caches.keys().then((cacheNames) => Promise.all(
      cacheNames.map((cacheName) => {
// 将不在白名单里面的缓存全部清理掉
        if (cacheWhitelist.indexOf(cacheName) === -1) {
          return caches.delete(cacheName);
        }
        return '';
      }),
    )),
  );
});

6 接入webpack

用Webpack构建接入Service Workers的离线应用时,要解决的关键 问题在于如何生成上面提到的sw.js文件。并且sw.js文件中的 cacheFileList变量,代表需要被缓存文件的URL列表,需要根据输出文 件列表所对应的URL来决定,而不是像上面那样写成静态值。

Webpack没有原生功能可以完成以上要求,幸好庞大的社区中已经 有人为我们做好一个插件serviceworker-webpack- plugin

npm i serviceworker-webpack-plugin --D

const serviceworkerWebpackPlugin = require('serviceworker-webpack-plugin');
plugins: [
    new CleanWebpackPlugin(), // 每次build,先清楚之前的dist文件夹
    new ServiceWorkerWebpackPlugin({
      // 插件会把入口文件列表注入sw.js文件中
      entry: path.resolve(__dirname, './sw.js'),
    }),
  ],

在以上配置中有两点需要注意:
· 由于Service Workers必须在HTTPS环境下才能拦截网络请求来实

现离线缓存,所以这里通过在2.6节中提到的方式去实现HTTPS服务;

· serviceworker-webpack-plugin 插件为了保证灵活性,允许使用者 自定义 sw.js,构建输出的 sw.js 文件中会在头部注入一个变量 serviceWorkerOption.assets到全局,里面存放着所有需要被缓存的文件 的URL列表。

需要将上面的sw.js文件中被写成了静态值的cacheFileList修改如 下:

// 需要被缓存的文件的url列别
const cacheList = global.serviceWorkerOption.assets;

还有一点,需要使用https协议,

devServer: {
    ...
    https: true,

  };

 

执行npm run dev

报错了,这种情况需要以下面的方式来打开浏览器。

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=./tmp --ignore-certificate-errors --unsafely-treat-insecure-origin-as-secure=https://0.0.0.0:8001

 终端运行上面代码,关闭浏览器的安全验证,就可以打开了。

通过打开开发者工具的 Application-Service Workers 一 栏,就能看到当前页面注册的Service Workers,正常的效果如图:

打开开发者工具的Application-Cache-Cache Storage 一栏,就能看到当前页面缓存的资源列表,正常的效果如图

 

为了验证网页在离线时的访问能力,需要在开发者工具中的 Network 一栏通过 Offline选项禁用网络,再刷新页面使其能正常访问, 并且网络请求的响应都来自Service Workers,正常的效果如图:

注意这里要访问https://0.0.0.0:8001/index.html 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值