Service Worker 离线缓存

Service worker 本质上充当 Web 应用程序、浏览器与网络(可用时)之间的代理服务器。这个 API 旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用来采取适当的动作、更新来自服务器的的资源。它还提供入口以推送通知和访问后台同步 API。

Service worker 运行在 worker 上下文:因此它无法访问 DOM,相对于驱动应用的主 JavaScript 线程,它运行在其他线程中,所以不会造成阻塞。

出于安全考量,Service worker 只能由 HTTPS 或 localhost 使用。

注册

javascript 复制代码
if ('serviceWorker' in navigator) {
    navigator.serviceWorker
        .register('/service-worker.js', {
            scope: '/',
        })
        .then(function (registration) {
            // 注册成功
            console.log('ServiceWorker registered successful', registration.scope);
        })
        .catch(function (err) {
            // 注册失败
            console.log('ServiceWorker registration failed: ', err);
        });
}

scope 用来指定 ServiceWorker 的作用域,它的默认值是 ServiceWorker 脚本所在目录。

我们来看下控制台输出信息:

console 复制代码
ServiceWorker registered successful http://localhost:7016/

Service Worker 生命周期

  1. 下载
  2. 安装
  3. 激活
service-worker.js 复制代码
self.addEventListener('install', function (event) {
    console.log('install');
});

self.addEventListener('activate', function (event) {
    console.log('activate');
});

self.addEventListener('fetch', function (event) {
    console.log('fetch');
});

控制台打印:

刷新页面,控制台只剩下 fetch 事件,安装和激活只会在首次下载 Service Worker 时执行。

离线缓存

service-worker.js 复制代码
const addResourcesToCache = async (resources) => {
    const cache = await caches.open('v1');
    await cache.addAll(resources);
};

const putInCache = async (request, response) => {
    const cache = await caches.open('v1');
    await cache.put(request, response);
};

const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
    // First try to get the resource from the cache
    const responseFromCache = await caches.match(request);
    if (responseFromCache) {
        return responseFromCache;
    }

    // Next try to use the preloaded response, if it's there
    const preloadResponse = await preloadResponsePromise;
    if (preloadResponse) {
        console.info('using preload response', preloadResponse);
        putInCache(request, preloadResponse.clone());
        return preloadResponse;
    }

    // Next try to get the resource from the network
    try {
        const responseFromNetwork = await fetch(request);
        // response may be used only once
        // we need to save clone to put one copy in cache
        // and serve second one
        putInCache(request, responseFromNetwork.clone());
        return responseFromNetwork;
    } catch (error) {
        const fallbackResponse = await caches.match(fallbackUrl);
        if (fallbackResponse) {
            return fallbackResponse;
        }
        // when even the fallback response is not available,
        // there is nothing we can do, but we must always
        // return a Response object
        return new Response('Network error happened', {
            status: 408,
            headers: { 'Content-Type': 'text/plain' },
        });
    }
};

const enableNavigationPreload = async () => {
    if (self.registration.navigationPreload) {
        // Enable navigation preloads!
        await self.registration.navigationPreload.enable();
    }
};

self.addEventListener('activate', (event) => {
    console.log('activate');
    event.waitUntil(enableNavigationPreload());
});

self.addEventListener('install', (event) => {
    console.log('install');
    event.waitUntil(
        addResourcesToCache([
            '/',
            '/index.html',
            '/index.css',
            'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.12.1/polyfill.min.js',
            'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.16.2/highlight.min.js',
        ])
    );
});

self.addEventListener('fetch', (event) => {
    console.log('fetch', event.request);
    event.respondWith(
        cacheFirst({
            request: event.request,
            preloadResponsePromise: event.preloadResponse,
            fallbackUrl: '/fallback',
        })
    );
});

使用 CacheStorage 来进行缓存。

首次访问后,刷新页面,查看 Network, 发现已经改为从 ServiceWorker 加载缓存的资源文件

再来看下控制台

我们通过监听 fetch 事件进行请求拦截,它会拦截所有的请求。这里也在 fetch 监听函数中把请求给缓存了下来,但是只能缓存 Get 请求。

点击发送请求,看到请求首次发送之后,会进行缓存,再次点击会从 ServiceWorker 中获取。

我们断开网络,关闭浏览器后重新打开,发现页面仍能正常访问

点击发送请求,返回的是 ServiceWorker 中缓存的结果。

切换至 Application,看到缓存存放在 CacheStorage 中,不会随页面的关闭而销毁。

相关推荐
m0_748255025 分钟前
前端常用算法集合
前端·算法
真的很上进19 分钟前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web1309332039825 分钟前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2341 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1231 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~2 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语2 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport2 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg2 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全
胡西风_foxww2 小时前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest