您的位置:

深入了解ServiceWorker缓存

一、ServiceWorker缓存文件

ServiceWorker是一种可以在浏览器后台运行JavaScript脚本的API,它能够拦截网络请求并且将其转化为来自缓存的响应。这意味着ServiceWorker能够将资源缓存到本地,甚至可以使你的web应用离线使用!下面是一个简单的示例:


// 注册ServiceWorker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/serviceworker.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

// 缓存文件
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js',
        '/logo.png'
      ]);
    })
  );
});

// 返回缓存的响应
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request);
    })
  );
});

这段代码向我们说明ServiceWorker如何缓存文件。在ServiceWorker注册之后,它会通过install事件进行缓存。缓存文件的代码片段如下:


self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js',
        '/logo.png'
      ]);
    })
  );
});

我们首先打开一个名为'my-cache'的缓存。然后通过cache.addAll()方法将我们需要缓存的文件添加到缓存中。在事件的最后,我们需要调用event.waitUntil()以确保ServiceWorker在缓存过程中不会被中止。

如果我们尝试缓存已经存在的文件,如/,它将被忽略并不会影响后续步骤。一旦文件被成功缓存,我们可以在浏览器的开发工具中查看缓存的文件,如下图所示:

二、ServiceWorker缓存大小

ServiceWorker缓存大小与其他类型的缓存一样,是有限制的,不同浏览器之间也有所不同。

在Chrome中,ServiceWorker缓存的大小默认为50MB。在Firefox中,这个值默认为50MB,但是可以通过about:config进行更改。在Safari中,ServiceWorker缓存的大小默认为0,但是可以通过开启web开发者模式进行更改。

当缓存大小达到最大限制时,ServiceWorker将无法缓存新的文件并且会删除过期的文件以腾出空间。如果你使用缓存Api(caches.open()、cache.addAll()、cache.put()等),你可以通过cache.keys()方法查看缓存中的所有的键名并且去删除过期的文件。


// 清空过期的文件
self.addEventListener('activate', function(event) {
  var cacheKeeplist = ['my-cache'];
  
  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheKeeplist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});

上面的代码展示了如何清空过期的文件。它通过event.waitUntil()方法来等待Promise的返回结果,以保证ServiceWorker不会在删除过期文件时被中止。同时,我们只删除名为'my-cache'的缓存,其他缓存不受影响。

三、ServiceWorker缓存文件版本控制

对于每次缓存文件的更新,为了防止浏览器缓存旧的文件,你需要对ServiceWorker进行版本控制。这可以通过在ServiceWorker文件中添加一个字符串版本号来实现。

下面是一个示例:


// 版本号
var CACHE_NAME = 'my-cache-v1';

// 注册ServiceWorker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/serviceworker.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

// 缓存文件
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME).then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js',
        '/logo.png'
      ]);
    })
  );
});

// 返回缓存的响应
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request);
    })
  );
});

// 更新缓存
self.addEventListener('activate', function(event) {
  var cacheKeeplist = [CACHE_NAME];
  
  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheKeeplist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});

我们在这个例子中添加了一个版本号——'my-cache-v1',以避免缓存的文件与以前的版本相混淆。另外,在更新缓存时,应在缓存版本中更新版本号。

在实际开发过程中,当你进行任何修改时,都应该更改版本号。任何版本不同的ServiceWorker都将触发activate事件,以便删除旧缓存并更新到新缓存中。

在实际项目中,版本控制通常会涉及自动构建和更新版本号,以避免手动操作。

四、ServiceWorker缓存策略

缓存策略是指ServiceWorker决定返回哪个响应的算法。通常,它根据请求的URL及请求方式(GET、POST、PUT等)进行决策。

常见的缓存策略有:

  • networkFirst(网络优先):ServiceWorker优先检查网络是否可用,如果网络正常,则从网络中获取响应,如果网络不可用,则从缓存中获取响应。
  • cacheFirst(缓存优先):ServiceWorker优先检查缓存是否存在请求的资源,如果存在,则从缓存中获取响应,否则从网络中获取响应。
  • cacheOnly(仅缓存):ServiceWorker只从缓存中获取资源,如果没有缓存,则返回错误响应。
  • networkOnly(仅网络):ServiceWorker只从网络中获取资源,如果没有网络,则返回错误响应。
  • staleWhileRevalidate(过期更新):ServiceWorker优先从缓存中获取响应,并将其返回给浏览器,然后在后台发送请求以获取最新版本的响应,并更新缓存以供下次使用。

// 网络优先
self.addEventListener('fetch', function(event) {
  event.respondWith(
    fetch(event.request).catch(function() {
      return caches.match(event.request);
    })
  );
});

// 缓存优先
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request);
    })
  );
});

// 仅缓存
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
  );
});

// 仅网络
self.addEventListener('fetch', function(event) {
  event.respondWith(
    fetch(event.request)
  );
});

// 过期更新
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('my-cache').then(function(cache) {
      return cache.match(event.request).then(function(response) {
        var fetchPromise = fetch(event.request).then(function(networkResponse) {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });
        return response || fetchPromise;
      });
    })
  );
});

上面的代码展示了各种缓存策略的示例。你可以选择一个适合你的需求的缓存策略来使用。

结论

通过ServiceWorker缓存机制,你可以轻松地将资源缓存到本地,使得你的Web应用可以离线使用。在不同浏览器中,ServiceWorker大小,版本控制方式和缓存策略会有所不同,因此你需要针对不同的浏览器进行优化。缓存策略应根据你的网站的需求进行选择,以实现最佳的性能和用户体验。