到目前为止,本文主要介绍有关预缓存的内容,其中经常涉及 generateSW
和 injectManifest
构建工具。虽然有许多理由在服务工件中添加预缓存逻辑,但您不必使用预缓存即可使用 Workbox。
也许您的项目只需要运行时缓存,或者您希望采用更简洁的方式来集成 Service Worker API,例如 Web 推送。在这些情况下,您不想使用 Workbox 的构建工具,本文将介绍这类情况。
使用捆绑器时
在 Web 开发领域,打包工具占据着重要地位,您的项目很有可能在使用打包工具。请务必注意,如果您不预缓存任何内容,则无需使用捆绑器插件(例如 workbox-webpack-plugin
)。您将把 Service Worker 视为应用中的单独入口点。
在项目源代码目录的根目录中,您将创建一个服务工件,并使用应用所需的任何 Workbox 模块。下面的示例不进行预缓存,而是在单独的 Cache
实例中为导航栏和图片素材资源请求设置缓存策略:
// sw.js
import {NetworkFirst, CacheFirst} from 'workbox-strategies';
import {registerRoute, NavigationRoute, Route} from 'workbox-routing';
const navigationRoute = new NavigationRoute(new NetworkFirst({
cacheName: 'navigations'
}));
const imageAssetRoute = new Route(({request}) => {
return request.destination === 'image';
}, new CacheFirst({
cacheName: 'image-assets'
}));
registerRoute(navigationRoute);
registerRoute(imageAssetRoute);
接下来,只需在您选择的捆绑工具中将此服务工件指定为入口点即可。下面举例说明了如何在一些常用的捆绑器中执行此操作。
webpack
webpack 在其 entry
配置中接受入口点。使用此方法时,请注意以下几点:
- 为确保您的 Service Worker 具有尽可能广泛的范围,您需要将其输出到输出目录的根目录。
- 您不希望对 Service Worker 进行版本控制,因为对其进行更新会生成新的哈希,这可能会导致您的网站上部署多个 Service Worker。
为了满足上述条件,可以传递一个函数到 output.filename
,以检查正在处理的当前入口点是否为 Service Worker 入口点。否则,版本化文件会写入其正常目标位置。
// webpack.config.js
import process from 'process';
const isProd = process.env.NODE_ENV === 'production';
export default {
mode: isProd ? 'production' : 'development',
context: process.cwd(),
entry: {
// Service worker entry point:
sw: './src/sw.js',
// Application entry point:
app: './src/index.js'
},
output: {
filename: ({runtime}) => {
// Check if the current filename is for the service worker:
if (runtime === 'sw') {
// Output a service worker in the root of the dist directory
// Also, ensure the output file name doesn't have a hash in it
return '[name].js';
}
// Otherwise, output files as normal
return 'js/[name].[contenthash:8].js';
},
path: './dist',
publicPath: '/',
clean: true
}
};
汇总
Rollup 与 webpack 类似,但不同的是,多个入口点被指定为数组中导出的单独配置对象:
// rollup.config.js
import { nodeResolve } from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
// Plugins common to both entry points
const plugins = [
nodeResolve(),
];
export default [
// Application entry point
{
input: './src/index.js',
output: {
dir: './dist/js',
format: 'esm'
},
plugins
},
// Service worker entry point
{
input: './src/sw.js',
output: {
file: './dist/sw.js',
format: 'iife'
},
plugins: [
...plugins,
// This @rollup/plugin-replace instance replaces process.env.NODE_ENV
// statements in the Workbox libraries to match your current environment.
// This changes whether logging is enabled ('development') or disabled ('production').
replace({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production')
})
]
}
];
esbuild
esbuild 提供了简单明了的命令行界面:
npx esbuild ./src/sw.js --bundle --minify --outfile=./dist/sw.js
默认情况下,esbuild 会将 process.env.NODE_ENV 替换为“development”,如果启用了缩减功能,则会替换为“production”。
如果没有使用 workbox-sw
的捆绑器
您的项目甚至可能不使用捆绑器。如果您使用 importScripts
导入 Workbox 运行时,workbox-sw
可以从 Service Worker 内的 CDN 中为您加载该运行时,并且无需执行构建步骤:
// sw.js
// Imports Workbox from the CDN. Note that "6.2.0" of the URL
// is the version of the Workbox runtime.
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.2.0/workbox-sw.js');
const navigationRoute = new workbox.routing.NavigationRoute(new workbox.strategies.NetworkFirst({
cacheName: 'navigations'
}));
const imageAssetRoute = new workbox.routing.Route(({request}) => {
return request.destination === 'image';
}, new workbox.strategies.CacheFirst({
cacheName: 'image-assets'
}));
workbox.routing.registerRoute(navigationRoute);
workbox.routing.registerRoute(staticAssetRoute);
如果从 CDN 加载 Workbox 运行时不太可行,您可以将 workbox-sw
与本地网址搭配使用。
总结
现在,您已经知道如何在不预缓存的情况下使用 Workbox,因此您不再受特定捆绑器或构建工具的约束。这让您可以灵活地使用自己感兴趣的 Workbox 运行时缓存代码来手动构建 Service Worker。