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 中,不会随页面的关闭而销毁。

相关推荐
正小安23 分钟前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch2 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光2 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   2 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   2 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web2 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常2 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇3 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr3 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho4 小时前
【TypeScript】知识点梳理(三)
前端·typescript