chaihongjun.me

serviceworker配置优化版

之前的文章介绍的缓存方案是白名单形式,只把需要缓存的内容列举出来进行缓存,这篇文章反向思考使用黑名单模式,黑名单里的不缓存,其他的都缓存,一般不缓存的都是第三方域名下的跨域的资源,也无法缓存。

e5871dd9d89130c4d42696576a40a84d.gif

延迟注册serviceworker(每个页面都要加载):

window.addEventListener('load', function() {
  if('serviceWorker' in navigator){
     // serviceworker.js 文件要放在网站发布的根目录
     navigator.serviceWorker.register('/serviceworker.js').then(function (registration) {
      console.log('Service Worker Registered,register script: serviceworker.js.');
    }).catch(function (error) {
      // registration failed
      console.log('Registration failed with ' + error);
    });
  }
});
// 控制台显示service worker缓存占用情况
  if ("storage" in navigator && "estimate" in navigator.storage) {
    // 检查浏览器是否支持 Web Storage 估计功能
    navigator.storage
      .estimate()
      .then((estimate) => {
        // 转换使用量和配额到 MB
        const usageMB = estimate.usage / 1024 / 1024;
        const quotaMB = estimate.quota / 1024 / 1024;

        // 计算使用比例
        const proportion = (estimate.usage / estimate.quota) * 100;

        // 打印结果
        console.log(
          `Using ${usageMB.toFixed(2)} out of ${quotaMB.toFixed(
            2
          )} MB. The proportion is ${proportion.toFixed(2)}%`
        );
      })
      .catch((error) => {
        // 处理可能的错误
        console.error("Error estimating storage:", error);
      });
  } else {
    // 如果不支持,可以在这里打印一条消息
    console.log("Browser does not support storage estimation.");
  }

这个和之前的一样没有什么大的变化。

然后是serviceworker.js一定是放在网站发布目录的根目录,保证它的作用域最大化。具体的配置发生了变化:

//serviceworker.js
'use strict';
const version = 'v20180608';   //缓存版本号,修改之后缓存会刷新
const __DEVELOPMENT__ = false;    //开发模式
const __DEBUG__ = false;       //调试模式
const offlineResources = [ //离线断网的时候提供友好界面
    '/',
    '/offline.html',
    '/offline.svg'
];
const ignoreFetch = [ //这里是需要忽略不缓存的的第三方的域名
    /chrome-extension:\/\//,                  // 建议加上忽略chrome插件的协议
    /https?:\/\/ae.bdstatic.com\//,
        /https?:\/\/msite.baidu.com\//,
        /https?:\/\/s.bdstatic.com\//,
        /https?:\/\/timg01.bdimg.com\//,
        /https?:\/\/zz.bdstatic.com\//,
        /https?:\/\/jspassport.ssl.qhimg.com\//,
        /https?:\/\/hm.baidu.com\//,
        /https?:\/\/sp0.baidu.com\//,
        /https?:\/\/s.360.cn\//,
        /https?:\/\/s.ssl.qhres.com\//,
        /https?:\/\/www.google-analytics.com\//,
        /https?:\/\/www.googletagmanager.com\//,
        /.php$/ 
];
//////////
// Install
//////////
function onInstall(event) {
    log('install event in progress.');
    event.waitUntil(updateStaticCache());
}

function updateStaticCache() {
    return caches
        .open(cacheKey('offline'))
        .then((cache) => {
            return cache.addAll(offlineResources);
        })
        .then(() => {
            log('installation complete!');
        });
}
////////
// Fetch
////////
function onFetch(event) {
    const request = event.request;
    if (shouldAlwaysFetch(request)) {
        event.respondWith(networkedOrOffline(request));
        return;
    }
    if (shouldFetchAndCache(request)) {
        event.respondWith(networkedOrCached(request));
        return;
    }
    event.respondWith(cachedOrNetworked(request));
}

function networkedOrCached(request) {
    return networkedAndCache(request)
        .catch(() => { return cachedOrOffline(request) });
}
// Stash response in cache as side-effect of network request
function networkedAndCache(request) {
    return fetch(request)
        .then((response) => {
            var copy = response.clone();
            caches.open(cacheKey('resources'))
                .then((cache) => {
                    cache.put(request, copy);
                });
            log("(network: cache write)", request.method, request.url);
            return response;
        });
}

function cachedOrNetworked(request) {
    return caches.match(request)
        .then((response) => {
            log(response ? '(cached)' : '(network: cache miss)', request.method, request.url);
            return response ||
                networkedAndCache(request)
                .catch(() => { return offlineResponse(request) });
        });
}

function networkedOrOffline(request) {
    return fetch(request)
        .then((response) => {
            log('(network)', request.method, request.url);
            return response;
        })
        .catch(() => {
            return offlineResponse(request);
        });
}

function cachedOrOffline(request) {
    return caches
        .match(request)
        .then((response) => {
            return response || offlineResponse(request);
        });
}

function offlineResponse(request) {
    log('(offline)', request.method, request.url);
    if (request.url.match(/.(jpg|png|gif|svg|jpeg)(?.*)?$/)) {
        return caches.match('/offline.svg');
    } else {
        return caches.match('/offline.html');
    }
}
///////////
// Activate
///////////
function onActivate(event) {
    log('activate event in progress.');
    event.waitUntil(removeOldCache());
}

function removeOldCache() {
    return caches
        .keys()
        .then((keys) => {
            return Promise.all( // We return a promise that settles when all outdated caches are deleted.
                keys
                .filter((key) => {
                    return !key.startsWith(version); // Filter by keys that don't start with the latest version prefix.
                })
                .map((key) => {
                    return caches.delete(key); // Return a promise that's fulfilled when each outdated cache is deleted.
                })
            );
        })
        .then(() => {
            log('removeOldCache completed.');
        });
}

function cacheKey() {
    return [version, ...arguments].join(':');
}

function log() {
    if (developmentMode()) {
        console.log("SW:", ...arguments);
    }
}

function shouldAlwaysFetch(request) {
    return __DEVELOPMENT__ ||
        request.method !== 'GET' ||
        ignoreFetch.some(regex => request.url.match(regex));
}

function shouldFetchAndCache(request) {
    return ~request.headers.get('Accept').indexOf('text/html');
}

function developmentMode() {
    return __DEVELOPMENT__ || __DEBUG__;
}
log("Hello from ServiceWorker land!", version);
self.addEventListener('install', onInstall);
self.addEventListener('fetch', onFetch);
self.addEventListener("activate", onActivate);

注意到上面的配置文件里面有提到一个离线文件offline.html,这里给出一个简单的版本:

    
    

    
        
        
        
        
    网络异常 | Powered by chaihongjun.me    
        
    

 
    

网络异常

         

你的网络似乎遭遇了中断,无法从 chaihongjun.me 获取最新的数据。

         

由于 Service Worker 技术,你之前访问过的页面已被缓存,可以从当前设备上离线访问,但部分资源(图片、样式等)可能无法显示。

         返回上一页         

若已确认你的网络环境正常,可以点击下方的按钮或按浏览器刷新按钮,来重载当前页面。

         立即重载         


知识共享许可协议本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。作者:柴宏俊»serviceworker配置优化版