魔改 sw-precache

sw-precache 是一个用来生成预加载资源的 ServiceWorker 的 js 文件的模块。将它用到 Ghost 上后,发现它并不能正常工作,原来 Ghost 会自动给 asset 中的静态文件加上 ?v=#######,而 sw-precache 里记录的是原始的文件名,并不匹配。所以对 sw.js 修改了一下。

// sw.js

var url = event.request.url;
if (/\.(js|css)(\?|$)/.test(url) && /^https?:\/\/(blog\.ihanai\.com|cdnjs\.cloudflare\.com)/.test(url)) {
  event.respondWith(
    caches.open(cacheName).then(function (cache) {
      return setOfCachedUrls(cache).then(function (cachedUrls) {
        // If we don't have a key matching url in the cache already, add it.
        var cacheKey = event.request.url;
        if (!cachedUrls.has(cacheKey)) {
          return fetch(event.request).then(function (response) {
            if (response.ok || response.type === 'opaque') {
              cleanResponse(response).then(function (responseToCache) {
                return cache.put(cacheKey, responseToCache);
              });
            }
            return response.clone();
          });
        } else {
          return cache.match(cacheKey).then(function (response) {
              if (response) return response;
              throw Error('The cached response that was expected is missing.');
            })
            .catch(function (e) {
              console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
              return fetch(event.request);
            });
        }
      });
    })
  );
}

这里有点需要注意的是使用 response.clone() 避免 TypeError。

当然,这样改了之后,功能变了,由原来的预加载资源文件变成了将部分文件使用 ServiceWorker 作缓存了。

最简单的方法还是修改 sw-precache 的配置,增加 ignoreUrlParametersMatching: [/^utm_/, /^v$/],这样 sw-precache 就能忽略 assetHash 了。由于生成的 sw.js 文件里有对资源 hash 的记录,所以忽略 Ghost 生成的 assetHash 并不会有什么不好的影响。

说点其它的,使用上面所说的方法将所以 js、css 资源文件用 ServiceWorker 缓存之后发现较 from disk cache 变慢了,原因在于随着 fetch 事件触发次数的增加,sw.js 中的一些代码如 setOfCachedUrlscaches.open 执行了一次又一次,很多结果是相同的,但是并没有缓存,所以实际使用中可以考虑对其进行定制化修改。