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

相关推荐
西陵5 分钟前
为什么说 AI 赋能前端开发,已经不是选择题,而是必然趋势?
前端·架构·ai编程
by__csdn1 小时前
Vue3 setup()函数终极攻略:从入门到精通
开发语言·前端·javascript·vue.js·性能优化·typescript·ecmascript
天天扭码1 小时前
前端如何实现RAG?一文带你速通,使用RAG实现长期记忆
前端·node.js·ai编程
Luna-player2 小时前
在前端中,<a> 标签的 href=“javascript:;“ 这个是什么意思
开发语言·前端·javascript
lionliu05192 小时前
js的扩展运算符的理解
前端·javascript·vue.js
小草cys2 小时前
项目7-七彩天气app任务7.4.2“关于”弹窗
开发语言·前端·javascript
奇舞精选2 小时前
GELab-Zero 技术解析:当豆包联手中兴,开源界如何守住端侧 AI 的“最后防线”?
前端·aigc
奇舞精选2 小时前
Vercel AI SDK:构建现代 Web AI 应用指南
前端·aigc
神仙别闹3 小时前
基于C语言实现B树存储的图书管理系统
c语言·前端·b树
玄魂3 小时前
如何查看、生成 github 开源项目star 图表
前端·开源·echarts