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

相关推荐
程序员爱技术1 小时前
Vue 2 + JavaScript + vue-count-to 集成案例
前端·javascript·vue.js
并不会2 小时前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
悦涵仙子2 小时前
CSS中的变量应用——:root,Sass变量,JavaScript中使用Sass变量
javascript·css·sass
衣乌安、2 小时前
【CSS】居中样式
前端·css·css3
兔老大的胡萝卜2 小时前
ppk谈JavaScript,悟透JavaScript,精通CSS高级Web,JavaScript DOM编程艺术,高性能JavaScript pdf
前端·javascript
低代码布道师2 小时前
CSS的三个重点
前端·css
耶啵奶膘4 小时前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^5 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie6 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic6 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js