实现service worker
离线缓存以前需要自己编写sw.js
文件内容,比较复杂。 谷歌提供了workbox-*
库来协助写配置。
vite-plugin-pwa就是帮你使用workbox
结合一些模板代码自动生成sw.js
,实现0配置
// vite.config.ts/js
import { VitePWA } from "vite-plugin-pwa";
export default {
plugins: [
VitePWA()
]
}
function VitePWA(userOptions?: Partial<VitePWAOptions>): Plugin[];
interface VitePWAOptions {
// @default process.env.NODE_ENV or "production"
mode?: 'development' | 'production';
// @default 'public'
srcDir?: string;
// @default 'dist'
outDir?: string;
// @default 'sw.js'
filename?: string;
// @default 'manifest.webmanifest'
manifestFilename?: string;
// @default 'generateSW'
strategies?: 'generateSW' | 'injectManifest';
// @default same as `base` of Vite's config
scope?: string;
//`inline` - inject a simple register, inlined with the generated html
//`script` - inject in , with the `sr` to a generated simple register
//`null` - do nothing, you will need to register the sw you self, or imports from `virtual:pwa-register`
// @default 'auto'
injectRegister: 'inline' | 'script' | 'auto' | null | false;
// `prompt` - you will need to show a popup/dialog to the user to confirm the reload.
// `autoUpdate` - when new content is available, the new service worker will update caches and reload all browser
// @default 'prompt'
registerType?: 'prompt' | 'autoUpdate';
// @default true
minify: boolean;
manifest: Partial<ManifestOptions> | false;
// @default false
useCredentials?: boolean;
workbox: Partial<GenerateSWOptions>;
injectManifest: Partial<CustomInjectManifestOptions>;
// @default "base" options from Vite
base?: string;
includeAssets: string | string[] | undefined;
// @default true
includeManifestIcons: boolean;
// @default false
disable: boolean;
devOptions?: DevOptions;
// @default false
selfDestroying?: boolean;
}
这里只重点介绍几个配置,其他感兴趣的自行查阅文档:
strategies
: 默认是generateSW
然后去配置workbox
;,如果你需要更多自定义的设置,可以选择injectManifest
,那就对应配置injectManifest
workbox
: 是对应generateSW
的配置,所有的workbox
配置,最终将生成`sw.js文件type GenerateSWOptions = BasePartial & GlobPartial & GeneratePartial & RequiredSWDestPartial & OptionalGlobDirectoryPartial;
export interface GlobPartial {
/**
* A set of patterns matching files to always exclude when generating the
* precache manifest. For more information, see the definition of `ignore` in
* the `glob` [documentation](https://github.com/isaacs/node-glob#options).
* @default ["**\/node_modules\/**\/*"]
*/
globIgnores?: Array<string>;
/**
* Files matching any of these patterns will be included in the precache
* manifest. For more information, see the
* [`glob` primer](https://github.com/isaacs/node-glob#glob-primer).
* @default ["**\/*.{js,css,html}"]
*/
globPatterns?: Array<string>;
}
重点关注:
globPatterns
: 最开始我们说配置会默认缓存所有的js.css.html,我是就是通过globPatterns
实现的。如果想增加图片缓存可以在里面添加globIgnores
: 忽略不想缓存的资源export interface GeneratePartial {
/**
* Any search parameter names that match against one of the RegExp in this
* array will be removed before looking for a precache match. This is useful
* if your users might request URLs that contain, for example, URL parameters
* used to track the source of the traffic. If not provided, the default value
* is `[/^utm_/, /^fbclid$/]`.
*
*/
ignoreURLParametersMatching?: Array<RegExp>;
/**
* When using Workbox's build tools to generate your service worker, you can
* specify one or more runtime caching configurations. These are then
* translated to {@link workbox-routing.registerRoute} calls using the match
* and handler configuration you define.
*
* For all of the options, see the {@link workbox-build.RuntimeCaching}
* documentation. The example below shows a typical configuration, with two
* runtime routes defined:
*
* @example
* runtimeCaching: [{
* urlPattern: ({url}) => url.origin === 'https://api.example.com',
* handler: 'NetworkFirst',
* options: {
* cacheName: 'api-cache',
* },
* }, {
* urlPattern: ({request}) => request.destination === 'image',
* handler: 'StaleWhileRevalidate',
* options: {
* cacheName: 'images-cache',
* expiration: {
* maxEntries: 10,
* },
* },
* }]
*/
runtimeCaching?: Array<RuntimeCaching>;
/**
* Whether to create a sourcemap for the generated service worker files.
* @default true
*/
sourcemap?: boolean;
}
重点关注:
ignoreURLParametersMatching
:默认无法缓存html后面带参数的页面,加上它忽略参数就可以缓存了runtimeCaching
: 运行时缓存,可以自定义配置各种类型的缓存,最后我们会详细介绍sourcemap
: 是否生成sourcemap
,它记录了转换压缩后的代码所对应的转换前的源代码位置,方便定位问题// vite.config.ts/js
import { VitePWA } from 'vite-plugin-pwa'
export default {
plugins: [
VitePWA({
workbox:{
globIgnores: ['static/js/**'],
globPatterns: ["**/*.{js,css,html,ico,jpg,png,svg}"],
ignoreURLParametersMatching: [/.*/],
sourcemap:true,
}
})
]
}
随着项目越来越庞大或者需求越来越复杂,有时候我们需要,缓存某些特定的路径文件,或者请求类型,以及单独设置js或者html的缓存时间和个数,这个时候我们就需要用到runtimeCaching
配置
export interface RuntimeCaching {
/**
* This determines how the runtime route will generate a response.
* To use one of the built-in {@link workbox-strategies}, provide its name,
* like `'NetworkFirst'`.
* Alternatively, this can be a {@link workbox-core.RouteHandler} callback
* function with custom response logic.
*/
handler: RouteHandler | StrategyName;
/**
* The HTTP method to match against. The default value of `'GET'` is normally
* sufficient, unless you explicitly need to match `'POST'`, `'PUT'`, or
* another type of request.
* @default "GET"
*/
method?: HTTPMethod;
/**
* This match criteria determines whether the configured handler will
* generate a response for any requests that don't match one of the precached
* URLs. If multiple `RuntimeCaching` routes are defined, then the first one
* whose `urlPattern` matches will be the one that responds.
*
* This value directly maps to the first parameter passed to
* {@link workbox-routing.registerRoute}. It's recommended to use a
* {@link workbox-core.RouteMatchCallback} function for greatest flexibility.
*/
urlPattern: RegExp | string | RouteMatchCallback;
options?: {
/**
* Configuring this will add a
* {@link workbox-cacheable-response.CacheableResponsePlugin} instance to
* the {@link workbox-strategies} configured in `handler`.
*/
cacheableResponse?: CacheableResponseOptions;
/**
* If provided, this will set the `cacheName` property of the
* {@link workbox-strategies} configured in `handler`.
*/
cacheName?: string | null;
/**
* Configuring this will add a
* {@link workbox-expiration.ExpirationPlugin} instance to
* the {@link workbox-strategies} configured in `handler`.
*/
expiration?: ExpirationPluginOptions;
};
}
重点关注:
handler
: 取缓存的策略,有五种
type StrategyName = 'CacheFirst' | 'CacheOnly' | 'NetworkFirst' | 'NetworkOnly' | 'StaleWhileRevalidate';
CacheFirst
:缓存优先CacheOnly
:仅使用缓存中的资源NetworkFirst
:网络优先NetworkOnly
:仅使用正常的网络请求StaleWhileRevalidate
:从缓存中读取资源的同时发送网络请求更新本地缓存method
: 默认是缓存get请求的资源,想缓存post的可以配置
urlPattern
: 通过正则,字符或者函数形式匹配要缓存的资源类型
options
自定义配置项
cacheableResponse
: 缓存状态码正确的资源,比如200的cacheName
: 自定义缓存的类型名称expiration
: 设置缓存的时间,数量。超过数量就删除之前的想解决我最开始抛出的,前后端域名相同,导致的下载或者导出失败问题,可以做如下配置:
// vite.config.ts/js
import { VitePWA } from 'vite-plugin-pwa'
export default {
plugins: [
VitePWA({
workbox: {
maximumFileSizeToCacheInBytes: 50000000,
globPatterns: [],
runtimeCaching: [
{
// 路径url中包含ai-synergy或者auth,handler设置成NetworkOnly
urlPattern: /.*\/(ai-synergy|auth).*/,
handler: 'NetworkOnly',
options: {
cacheName: 'ai-synergy|auth',
cacheableResponse: {
statuses: [200]
}
}
}
]
}
})
]
}
最后是更加完善的自定义配置方案:
VitePWA({
workbox: {
globPatterns: [],
runtimeCaching: [
mode !== 'production'
? {
urlPattern: ({ url }) => url.origin === 'https://xxx.list.com',
handler: 'NetworkFirst',
options: {
cacheName: 'list',
cacheableResponse: {
statuses: [200]
}
}
}
: {
urlPattern: ({ url }) => url.origin === 'https://xxx.detail.com',
handler: 'NetworkFirst',
options: {
cacheName: 'detail',
cacheableResponse: {
statuses: [200]
}
}
},
{
urlPattern: /.*\/(ai-synergy|auth).*/,
handler: 'NetworkOnly',
options: {
cacheName: 'ai-synergy|auth',
cacheableResponse: {
statuses: [200]
}
}
},
{
urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
handler: 'CacheFirst',
options: {
cacheName: 'images',
expiration: {
// 最多20个图
maxEntries: 20
}
}
},
{
urlPattern: /.*\.js.*/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'js',
expiration: {
maxEntries: 30, // 最多缓存30个,超过的按照LRU原则删除
maxAgeSeconds: 30 * 24 * 60 * 60
},
cacheableResponse: {
statuses: [200]
}
}
},
{
urlPattern: /.*\.css.*/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'css',
expiration: {
maxEntries: 20,
maxAgeSeconds: 30 * 24 * 60 * 60
},
cacheableResponse: {
statuses: [200]
}
}
},
{
urlPattern: /.*\.html.*/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'html',
expiration: {
maxEntries: 20,
maxAgeSeconds: 30 * 24 * 60 * 60
},
cacheableResponse: {
statuses: [200]
}
}
}
]
},
})