如何创建一个简单的服务工作者来添加新项目以离线缓存?

How do I create a simple service worker to add new items to cache offline?

我想从一个简单的 API 中获取响应,用它来创建一系列 URL(数组),然后我想将其提供给服务人员,并让它添加每个进入离线存储。

作为背景,我对 sw-precache (sw-precache-webpack-plugin)、sw-toolbox 和一般 JavaScript 的经验有限,尽管这种情况正在发生变化。目前有一个 webpack + vue 设置从我很久以前构建的一个简单的 API 获取数据。

访问我网站的用户可能希望离线使用一种或多种类型的内容 - 这可能需要 30 个请求(30 个单独的 URL)添加到缓存,最多 4000 个以上。上限预计会随着时间的推移而增加。 运行 Cache.addAll() 我之前尝试过的方式使客户端崩溃 - 一次请求太多。我认为 importScripts 选项可能是我需要的东西,但我不确定从哪里开始。我如何编写一个脚本来作为我可以向其发送数组的服务工作者的补充?

总结一下:

在开发代码中:https://laoadventist.info

获取缓存 URL 的简单 API:https://laoadventist.info/api/r2

我想缓存的单个示例API结果(单击一个按钮需要数万个请求):.../api/d

更新:

我能够想出一个有点实用的解决方案...它并不完美,或者在 service worker 中,但它确实有效。欢迎提出改进建议。

var temparray
var tempposition

let processCache = function (array, position) {
  if (typeof array === 'undefined') {
    array = []
  }
  if (array.length > 0) {
    let chunk = array.shift()
    temparray = array
    tempposition = position
    caches.open('testing')
      .then((cache) => { cache.addAll([chunk]) })
      .then(function () { processCache(temparray, tempposition) })
  }
}

如果您使用的是 service worker,那么您只需要获取 service-worker 对象并注册 service worker 文件。

我在这方面做了一些代码:线下体验。 Here's那个repo的代码,希望对你有帮助。

if ('serviceWorker' in navigator) {
  // registering the service worker file
  navigator.serviceWorker.register('./service-worker.js', {
    scope: './'                                                     // optional
  }).then(function (reg) {
    console.log('Service Worker registered successfully!');
  }).catch(function (err) {
    console.log('Service Worker failed to register', err);
  });
}

var name = 'simple nodexperts cache v2',
    filesToCache = [
      './index.html',
      './css/style.css',
      './script.js',
      './service-worker.js'
    ];

self.addEventListener('install', function (event) {
  console.log("installing");
  event.waitUntil(
    // opens cache
    // cache is an object which is available inside service-worker
    caches.open(name).then(function (cache) {
      return cache.addAll(filesToCache);
    })
  );
});

self.addEventListener('fetch', function (event) {
  console.log("fetching");
  event.respondWith(
    fetch(event.request).catch(function() {
      return caches.match(event.request);
    })
  );
});

更多信息你也可以查看这些博客,它们真的很不错:

选项 1:使用 webpack

Webpack 用户可以受益于 webpack-offline-plugin which is well maintained and does the job well. However, at runtime you will still need to write a little bit of code for dynamic caching. for dynamic caching, please refer to this advise by the plugin maintainer: here

选项 2:自己制作

在你的主包文件中注册 Service Worker

假设:您的服务工作者文件名为 sw.js,它位于您网站的根文件夹中。

if ('serviceWorker' in navigator) {
  // Recommended to register onLoad
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js');
  });
}

内部sw.js:安装静态

self.addEventListener('install', function(event) {
  self.skipWaiting();
  event.waitUntil(
    caches.open('static-files-v1').then(function(cache) {
      return cache.addAll(['/', 'someFile.js', 'someFile.css']);
    })
  );
});

内部 sw.js:获取已安装的静态信息

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

更多高级步骤

内部 sw.js:获取时缓存动态 URL

fetch 事件将监听通过 HTTPS 获取到您网站的所有内容,请阅读我的评论以帮助理解 snipper。

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('dynamic-content-v1').then(function(cache) {
      // check if the requested URL is inside the dynamic-content-v1
      return cache.match(event.request).then(function (response) {
        // when found, respond with it.
        // when not found: return it as it is after taking a clone
        // and storing it, so next visit to the URL it will be there
        return response || fetch(event.request).then(function(response) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

如果您有数千个页面、内容和图像,默认情况下这种方法不是一个很好的做法。您不想让用户膨胀并使用您所有的 缓存存储配额 。请考虑仅缓存要首先显示的重要内容并保持最新,因为您不想发布过时的信息。

选项 3 和重要说明:

重要提示:您不需要软件即可将动态内容添加到缓存 API。他们都是两个不同的东西。随意从框架组件\异步路由或其他任何内容中获取并添加到缓存API。

关于您的意见和建议fiddle:

我不太确定我是否 100% 了解您在做什么。我可以看到递归函数,但不确定您为什么需要它或其中发生了什么。以下是有关如何从组件内部动态缓存的片段:

// in case browser doesn't support cache
var URL_2_CACHE = '/content/important.html';
if (!('caches' in window)) return;
fetch(new Request(URL_2_CACHE)).then(function(response){
return caches.open('dynamic-cahce-v1').then(function(cache) {
    // DONE!
    return cache.put(URL_2_CACHE, response);
  });
});

现在 HTML 页面已存储在缓存中,SW 将在下一次请求时从那里获取它。

您说您已经熟悉 sw-precache,所以我建议您使用 sw-precache-webpack-plugin,正如您提到的那样。

使用 runtimeCaching 为您希望离线可用的内容指定不同的缓存策略。

如果缓存太多东西,请使用 runtimeCaching 配置中的 maxEntries,如下所示:

new SWPrecacheWebpackPlugin({
  runtimeCaching: [{
    urlPattern: /\/media\//,
    handler: 'cacheFirst',
    options: {
      cache: {
        maxEntries: 100,  // <-- limit entires to 100 before recycling
        name: 'media-cache'
      }
    }
  }]
})