我正在使用 Webpack 5,我希望有一个 Service Worker 能够拦截获取请求并在本地返回响应,而无需访问网络。我还希望能够在 Service Worker 中导入 npm 模块。我曾经使用一个名为 serviceworker-webpack-plugin 的库来实现此目的,但它不再维护(并且当我使用它时会抛出错误)。 Webpack 文档建议使用 Workbox,但据我所知,这似乎只是为了在 Service Worker 中缓存资产。 有人可以告诉我 2020 年使用 Webpack 5 创建 Service Worker 的正确方法是什么吗?
将 Service Worker 添加到您的 webpack.config.js 条目字段
entry: {
'app': "./src/index.js",
'service-worker': "./src/service-worker.ts",
},
output: {
filename: "[name].js",
},
这将发出
dist/app.js
和 dist/service-worker.js
,并且可以在两者中导入内容。
serviceworker-webpack-plugin
还为 serviceworker 提供了一种查看其应缓存的所有捆绑文件列表的方法,但该功能无法直接使用,需要制作 webpack 插件才能获取。
2022 年答案
Webpack 几乎开箱即用地支持这一点。
https://webpack.js.org/guides/progressive-web-application/
这将为您提供 web pack 正在为您处理的资源的基本缓存。
您可以获得更高级的: https://developer.chrome.com/docs/workbox/modules/workbox-webpack-plugin/
请注意,这是使用谷歌的 Workbox。我已经在离线第一个应用程序中使用它多年,并且效果非常好。
Webpack 5 应该开箱即用地为您执行此操作,与其他工作人员类似:
将资源的
与new URL
/new Worker
结合使用时,webpack 会自动为 Web Worker 创建一个新的入口点。new SharedWorker/navigator.serviceWorker.register
.new Worker(new URL("./worker.js", import.meta.url))
选择语法是为了允许在没有捆绑器的情况下运行代码。此语法也可在浏览器中的本机 ECMAScript 模块中使用。
(来自https://webpack.js.org/blog/2020-10-10-webpack-5-release/#native-worker-support)
使用 webpack 4,我们的 Service Worker 代码如下所示:
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import downloadWorker from 'worker-plugin/loader!../workers/downloadHelper'
navigator.serviceWorker.register(downloadWorker).then( //...
使用 webpack 5,代码变成:
navigator.serviceWorker
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.register(new URL('../workers/downloadHelper.ts', import.meta.url))
.then( // ...
我们从 webpack 5 配置中删除了
worker-plugin
,v4 的代码在 import
语句中使用了它。
关键是使用
new URL()
- webpack 5 会将其解释为 Web Worker,并为此 Service Worker 创建一个块并正确链接 url。
我们必须添加
eslint-disable-next-line
和 @ts-ignore
,因为 navigator.serviceworker.register
的接口需要一个字符串,而不是 URL
。看起来 webpack 正确地发送了一个字符串,但是 TypeScript 似乎无法理解在 webpack 运行之前运行 TypeScript 的情况。
功能:用于执行繁重的计算或后台任务而不阻塞主线程
worker-loader
npm i worker-loader -D
{
test: /\.worker\.js$/,
use: {
loader: 'worker-loader',
options: {
filename: '[name].[contenthash].worker.js',
esModule: false,
},
},
},
功能:通过启用离线支持、缓存等功能来增强 Web 应用程序
workbox-webpack-plugin
npm i workbox-webpack-plugin -D
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
plugins: [
new WorkboxWebpackPlugin.GenerateSW({
swDest: 'service-worker.js',
maximumFileSizeToCacheInBytes: 20_000_000,
clientsClaim: true,
skipWaiting: true,
cleanupOutdatedCaches: true,
// here, we set the navigateFallback to '/',
// so that any missing urls are handled by react-router
// navigateFallback: '/',
include: [/\.html$/, /\.js$/, /\.css$/, /\.wasm$/],
exclude: [/\.js\.map$/, /\.css\.map$/],
additionalManifestEntries: [
{
url: 'manifest.json',
revision: hashFileContent(path.join('../../public', 'manifest.json')),
},
// Add other entries if needed
],
runtimeCaching: [
{
urlPattern: /\.(?:png|gif|jpg|svg|ico|woff2)$/i,
handler: 'CacheFirst',
options: {
cacheName: 'image-cache',
// Only cache 300 images.
expiration: {
maxEntries: 300,
maxAgeSeconds: 3600 * 24 * 7, // one week
},
},
},
{
urlPattern: /\.html$/i,
handler: 'NetworkFirst',
options: {
cacheName: 'index-cache',
},
},
{
urlPattern: /^https:\/\/fonts\.googleapis\.com/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'google-fonts-stylesheets',
expiration: {
maxEntries: 4,
},
},
},
{
urlPattern: /^https:\/\/fonts\.gstatic\.com/,
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts-webfonts',
cacheableResponse: {
statuses: [0, 200],
},
expiration: {
maxEntries: 30,
maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
},
},
},
],
})
]
不要把事情搞得太复杂。
只需 2 步即可让软件工作。创建一个 sw 并注册它。
创建一个像
sw.js
一样的.js文件并在其中写入:
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.open('mysite-dynamic').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;
});
}),
);
});
这就是 stale-while-revalidate 方法
现在就注册吧。
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}