如何使用 Service Worker 缓存虚拟文件?

How to use a Service Worker to cache a virtual file?

我正在尝试为我正在处理的样板项目实施服务工作者(https://github.com/jonnyasmar/gravity-bp 欢迎反馈!),但我遇到了障碍:(

问题:

我通过 ExpressJS 为这个样板提供 index.html 实际上是作为解释的 Twig 模板。但是,因为我在构建时生成服务工作人员资产,这就是我将其指向可缓存静态资产的地方,所以我无法弄清楚如何告诉服务工作人员我希望它缓存虚拟 index.html ExpressJS 在运行时提供的文件。

我最成功的尝试成功缓存了所有静态资产(包括构建时生成的 asset-manifest.json),但不会缓存虚拟 index.html

如果我将其转换为静态 html 文件,Service Worker 会成功缓存它。

如果您对答案感兴趣,请务必给这个问题点赞,以帮助提高知名度!

问题:

  1. 有没有办法更正我的代码来完成此操作?
  2. 这样做有什么问题吗?
  3. 如果对 #2 的回答是肯定的,您建议如何处理这个问题,为什么?

See the full source on GitHub.

相关代码:

webpack.config.js:

output: {
  filename: '[name].js',
    chunkFilename: '[chunkhash].js',
    path: path.resolve(__dirname, 'public'),
    publicPath: '/'
},
plugins: {
  new ManifestPlugin({
    fileName: 'asset-manifest.json',
  }),
  new SWPrecacheWebpackPlugin({
    cacheId: 'gravity-bp',
    dontCacheBustUrlsMatching: /\.\w{8}\./,
    filename: 'sw.js',
    minify: true,
    navigateFallback: 'index.html',
    stripPrefix: 'public/',
    swFilePath: 'public/sw.js',
    staticFileGlobs: [
      'public/index.html',
      'public/**/!(*map*|*sw*)',
    ],
  })
}

sw.ts:

const swUrl: string = 'sw.js';

export const register = (): void =>{
  if(process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator){
    const sw: ServiceWorkerContainer = navigator.serviceWorker;
    sw.register(swUrl).then(registration =>{
      registration.onupdatefound = (): any =>{
        const installer: ServiceWorker = registration.installing;
        installer.onstatechange = (): any =>{
          if(installer.state === 'installed'){
            if(sw.controller){
              console.log('New content available.');
            }else{
              console.log('Content cached for offline use.');
            }
          }
        };
      };
    }).catch((error) =>{
      console.error('Failed to register service worker:', error);
    });
  }
};

export const unregister = (): void =>{
  if('serviceWorker' in navigator){
    navigator.serviceWorker.ready.then(registration =>{
      registration.unregister();
    });
  }
};

server.ts:

import * as path from 'path';
const twig = require('twig').__express;
const express = require('express');
const compression = require('compression');

const pkg = require('../../package.json');
const version = pkg.version;

let app = express(),
  ip = '0.0.0.0',
  port = 3000,
  views = path.resolve('./src/views');

app.use(compression());
app.use(express.static('public'));
app.set('view engine', 'twig');
app.engine('.twig', twig);
app.set('views', views);

// Routes
app.get("*", function(req: any, res: any, next: any){
  // vars
  res.locals.version = version;

  res.render('index');
});

let server = app.listen(port, ip, function(){
  let host = server.address().address;
  let port = server.address().port;
  console.log('Gravity Boilerplate ready at http://%s:%s', host, port);
});

sw-precache-webpack-plugin documentation it talks about using sw-precache options内。您应该调查的一项是 dynamicUrlToDependencies 设置。有关详细信息,请参阅其中一些链接:

比如,也许从这个开始测试:

dynamicUrlToDependencies: {
  '/': 'MAGIC_STRING_HERE'
},

所以真的,你需要 configure the sw-precache WebPack plugin to load a server rendered page as the navigateFallback route