神奇的 Workbox 3

templatedUrls: {

‘/shell’: [‘dev/templates/app-shell.html’, ‘dev/**/*.css’],

},

// 要替换的预留代码区正则

injectionPointRegexp: /(.precacheAndRoute()\s*[\s*]\s*())/,

})

.catch(err => {

console.error(Unable to inject the precache manifest into sw.js);

throw err;

});

在构建文件中执行这段代码就会读取 app/sw.js 文件然后生成一个 dist/sw.js 文件含有注入的预缓存内容列表。

关于如何使用 injectManifest 方法可以查看 workbox-build 的 injectManifest 方法 [全部参数](()

workbox-webpack-plugin

有了 workbox-build 可想而知就能搞出很多实用的预缓存插件方案,比如 Webpack、Gulp 等插件,workbox 官方也提供了一个插件 workbox-webpack-plugin,只需要通过以下方式,就可以将插件安装到你的 webpack 项目中:

1

npm install --save-dev workbox-webpack-plugin

然后就只需要将插件添加到 Webpack 配置中就可以正常使用了,插件参数和 workbox-build 的 injectManifest 方法保持一致:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

const workboxPlugin = require(‘workbox-webpack-plugin’);

// …

webpack({

plugins: [

// …

new workboxPlugin({

swSrc: ‘./src/sw.js’,

swDest: ‘./dist/sw.js’,

globDirectory: ‘./dist/’,

globPatterns: [‘**/*.{html,js,css}’],

})

]

// …

});

这里有个地方和之前提到的替换预留的 app/sw.js 不一样:使用 workbox 提供的 Webpack 插件必须在 app/sw.js 中包含以下代码才能完成预缓存内容列表注入工作

1

workbox.precaching.precacheAndRoute(self.__precacheManifest || []);

当插件跑起来之后,会在 /dist/sw.js 中增加一段 importScripts() 代码用来引入一个模块,这个模块的内容就是 self.__precacheManifest,也就是预缓存内容列表的内容,具体的效果可以在项目中使用 workbox-webpack-plugin 尝试一下,看看 build 后的 dist/sw.js 结果就会比较清楚了。

路由请求缓存


路由请求缓存是通过文件路由匹配的模式分别对制定的路由文件做不同策略缓存的方式,这部分工作可以在 app/sw.js 中直接使用 workbox 提供的 workbox.routing.registerRoute API 完成,这个 API 可以理解为干了两件事情,一、通过请求路由配置匹配到指定待缓存文件或请求内容二、通过第二个参数的处理回调函数决定用何种策略来缓存匹配上的文件。有 三种 方法可以通过 workbox-route 来匹配一个请求 URL

  • 字符串方式
1

2

3

4

5

6

7

8

9

10

11

// 可以直接是当前项目下的绝对路径

workbox.routing.registerRoute(

‘/logo.png’,

handler // handler 是做缓存策略的回调函数,通常指后面所会降到的 ‘缓存策略函数’

);

// 也可以是完整的带完整 host 的 URL 路径,这里的 URL 必须是 https 的

workbox.routing.registerRoute(

‘https://some-host/some-path/logo.png’,

handler

);

  • 正则表达式方式
1

2

3

4

workbox.routing.registerRoute(

new RegExp(‘.*.js’), // 这里是任何正则都行,只要能匹配得上的请求路由地址

handler

);

  • 回调函数方式
1

2

3

4

5

6

7

8

9

10

// 通过回调函数来匹配请求路由将会让策略更加灵活

const matchFunction = ({url, event}) => {

// 如果请求路由匹配了就返回 true,也可以返回一个参数对象以供 handler 接收处理

return false;

};

workbox.routing.registerRoute(

matchFunction,

handler

);

上面讲到了匹配请求路由的三种方式,接下来可以讲讲如何处理匹配上的请求所返回的内容,也就是上面三种路由匹配方式的 《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】 handler,通常有两种做法:

  • 使用一种 workbox 通过 workbox.strategies API 提供的 缓存策略

  • 提供一个自定义返回带有返回结果的 Promise 的回调方法。

路由请求缓存策略

下面介绍一下 workbox 默认提供的几种缓存策略 API,这些 API 可以被当成 handler 使用。

Stale While Revalidate

这种策略的意思是当请求的路由有对应的 Cache 缓存结果就直接返回,在返回 Cache 缓存结果的同时会在后台发起网络请求拿到请求结果并更新 Cache 缓存,如果本来就没有 Cache 缓存的话,直接就发起网络请求并返回结果,这对用户来说是一种非常安全的策略,能保证用户最快速的拿到请求的结果,但是也有一定的缺点,就是还是会有网络请求占用了用户的网络带宽。可以像如下的方式使用 State While Revalidate 策略:

1

2

3

4

workbox.routing.registerRoute(

match, // 匹配的路由

workbox.strategies.staleWhileRevalidate()

);

Network First

这种策略就是当请求路由是被匹配的,就采用网络优先的策略,也就是优先尝试拿到网络请求的返回结果,如果拿到网络请求的结果,就将结果返回给客户端并且写入 Cache 缓存,如果网络请求失败,那最后被缓存的 Cache 缓存结果就会被返回到客户端,这种策略一般适用于返回结果不太固定或对实时性有要求的请求,为网络请求失败进行兜底。可以像如下方式使用 Network First 策略:

1

2

3

4

workbox.routing.registerRoute(

match, // 匹配的路由

workbox.strategies.networkFirst()

);

Cache First

这个策略的意思就是当匹配到请求之后直接从 Cache 缓存中取得结果,如果 Cache 缓存中没有结果,那就会发起网络请求,拿到网络请求结果并将结果更新至 Cache 缓存,并将结果返回给客户端。这种策略比较适合结果不怎么变动且对实时性要求不高的请求。可以像如下方式使用 Cache First 策略:

1

2

3

4

workbox.routing.registerRoute(

match, // 匹配的路由

workbox.strategies.cacheFirst()

);

Network Only

比较直接的策略,直接强制使用正常的网络请求,并将结果返回给客户端,这种策略比较适合对实时性要求非常高的请求。可以像如下方式使用 Network Only 策略:

1

2

3

4

workbox.routing.registerRoute(

match, // 匹配的路由

workbox.strategies.networkOnly()

);

Cache Only

这个策略也比较直接,直接使用 Cache 缓存的结果,并将结果返回给客户端,这种策略比较适合一上线就不会变的静态资源请求。可以像如下方式使用 Cache Only 策略:

1

2

3

4

workbox.routing.registerRoute(

match, // 匹配的路由

workbox.strategies.networkOnly()

);

无论使用何种策略,你都可以通过自定义一个缓存来使用或添加插件(后面我们会介绍 workbox 插件)来定制路由的行为(以何种方式返回结果)。

1

2

3

4

5

6

7

8

9

workbox.strategies.staleWhileRevalidate({

// 使用用户自定义的缓存名称

cacheName: ‘my-cache-name’,

// 使用 workbox 插件

plugins: [

// …

]

});

当然,这些配置通常需要在缓存请求时更安全,也就是说,需要限制缓存的时间或者确保设备上用的数据是被限制的。

自定义策略

如果以上的那些策略都不太能满足你的请求的缓存需求,那就得想想办法自己定制一个合适的策略,甚至是不同情况下返回不同的请求结果,workbox 也考虑到了这种场景(这也是为什么我会极力推荐 workbox 的原因),当然,最简单的方法是直接在 Service Worker 文件里通过最原始的 fetch 事件控制缓存策略。也可以使用 workbox 提供的另一种方式:传入一个带有对象参数的回调函数,对象中包含匹配的 url 以及请求的 fetchEvent 参数,回调函数返回的就是一个 response 对象,具体用法如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

workbox.routing.registerRoute(

({url, event}) => {

return {

name: ‘workbox’,

type: ‘guide’,

};

},

({url, event, params}) => {

// 返回的结果是:A guide on workbox

return new Response(

A ${params.type} on ${params.name}

);

}

);

第三方请求的缓存


如果有些请求的域和当前 Web 站点不一致,那可以被认为是第三方资源或请求,针对第三方请求的缓存,因为 Workbox 无法获取第三方路由请求的状态,当请求失败的情况下 workbox 也只能选择缓存错误的结果,所以 workbox 3 原则上默认不会缓存第三方请求的返回结果。也就是说,默认情况下如下的缓存策略是不会生效的:

1

2

3

4

workbox.routing.registerRoute(

‘https://notzoumiaojiang.com/example-script.min.js’,

workbox.strategies.cacheFirst(),

);

当然,并不是所有的策略在第三方请求上都不能使用,workbox 3 可以允许 networkFirst 和 stalteWhileRevalidate 缓存策略生效,因为这些策略会有规律的更新缓存的返回内容,毕竟每次请求后都会更新缓存内容,要比直接缓存安全的多。

如果你强制使用 workbox 3 不推荐的缓存策略去缓存第三方请求,那 workbox 在 DevTools 里的 console 中会报警报哦。

如果你还是执意要缓存第三方请求的结果的话,workbox 3 也考虑到了确实会有这种难以扼杀的需求,提供了一个非常人性化的方式满足需求:利用 workbox.cacheableResponse.Plugin 来指定只缓存请求成功的结果,这样就打消掉我们之前对于不安全结果被缓存的顾虑了。鹅妹子英!(后面我们会介绍插件机制)

1

2

3

4

5

6

7

8

9

10

11

workbox.routing.registerRoute(

‘https://notzoumiaojiang.com/example-script.min.js’,

workbox.strategies.cacheFirst({

plugins: [

// 这个插件是让匹配的请求的符合开发者指定的条件的返回结果可以被缓存

new workbox.cacheableResponse.Plugin({

statuses: [0, 200]

})

]

}),

);

workbox 配置


workbox 3 提供了一些配置项,都封装在 workbox.core API 中,可以稍微了解一下。

配置缓存名称

通过 DevTools -> Applications -> Caches 可以发现,workbox 对于缓存命名有一些规则的:

1

<prefix>-<ID>-<suffix>

每个命名都有个前缀和后缀,中间才是真正的命名 ID,主要是为了更大限度的防止重名的情况发生,可以通过以下这种方式分别对 precache 和 runtime 形式的缓存进行自定义命名:

1

2

3

4

5

6

workbox.core.setCacheNameDetails({

prefix: ‘my-app’,

suffix: ‘v1’,

precache: ‘custom-precache-name’,// 不设置的话默认值为 ‘precache’

runtime: ‘custom-runtime-name’ // 不设置的话默认值为 ‘runtime’

});

通过以上设置后,precache 类型的缓存名称为 my-app-custom-precache-name-<ID>-v1,runtime 类型的缓存名称为 my-app-custom-runtime-name-<ID>-v1。workbox 推荐尽量为你的每个项目设置不同的 prefix,这样你在本地 locahost 调试 Service Worker 的时候可以避免冲突。而 suffix 可以用来控制缓存版本,让站点的 Service Worker 更新机制变得清晰维护。

workbox 为了让 Web App 的缓存管理的更加细粒度的清晰可维护,也提供了策略级别的缓存命名设置,可以通过策略 API 的 cacheName 参数进行设置:

1

2

3

4

5

6

workbox.routing.registerRoute(

/.*.(?:png|jpg|jpeg|svg|gif)/g,

new workbox.strategies.CacheFirst({

cacheName: ‘my-image-cache’,

})

);

这样,对应的图片相关的 cacheFirst 策略的缓存都会以 my-image-cache-<ID> 的形式命名,这里要注意的是:prefix 和 suffix 是不需要设置的

指定 development 环境

workbox 开发过程中是需要 debug 的,所以 workbox 3 也提供了 logger 机制帮助我们排查问题,但是在生产环境下,我们不希望也产生 logger 信息,所以 workbox 提供了「指定当前环境」 的设置:

1

2

3

4

5

// 设置为开发模式

workbox.setConfig({debug: true});

// 设置为线上生产模式

workbox.setConfig({debug: false});

配置日志 Level

workbox 3 提供 logger 机制帮助我们更好的调试 workbox,一共有四种 log level:debuglogwarnerror

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k0gq5KHw-1651215081371)(https://zoumiaojiang.com/article/amazing-workbox-3/workbox-core-logs.png)]uploading.4e448015.gif转存失败[重新上传](()[取消](()[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-21iSdgiT-1651215081372)(https://zoumiaojiang.com/article/amazing-workbox-3/workbox-core-logs.png)]uploading.4e448015.gif正在上传…[重新上传](()[取消](()workbox-core-logs.pnguploading.4e448015.gif正在上传…[重新上传](()[取消](()[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bHNGew2-1651215081373)(https://zoumiaojiang.com/article/amazing-workbox-3/workbox-core-logs.png)]uploading.4e448015.gif正在上传…[重新上传](()[取消](()workbox-core-logs.pnguploading.4e448015.gif正在上传…[重新上传](()[取消](()workbox logs

可以通过以下方式设置 log 的 level,这样就可以只看到某个 level 的 log 信息,让调试的过程中更加专注。具体的设置方式是通过 worbox.core API 中的 setLogLevel 方法来完成:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// 展示所有的 log

workbox.core.setLogLevel(workbox.core.LOG_LEVELS.debug);

// 只展示 log, warning 和 error 类型的 log

workbox.core.setLogLevel(workbox.core.LOG_LEVELS.log);

// 只展示 warning 和 error 类型的 log

workbox.core.setLogLevel(workbox.core.LOG_LEVELS.warn);

// 只展示 error 类型的 log

workbox.core.setLogLevel(workbox.core.LOG_LEVELS.error);

// 啥 log 都没有,这个适用于线上生产环境

workbox.core.setLogLevel(workbox.core.LOG_LEVELS.silent);

workbox 插件


插件机制应该是 workbox 3 最大的改进了,在 2.x 中,现有的插件大部分功能都是直接以 API 的形式抛给开发者,让开发者一头雾水,现在 3.0 中每个内置插件自己封装了之前的 API,对开发者来说专门解决了一个个独立的问题。除此之外,workbox 3 还提供插件扩展机制以及事件钩子可以让开发者自己扩展插件。workbox 插件可以让你通过操作一个请求的生命周期中的返回内容和请求内容添加其他的一些行为。

workbox 插件通常都是在缓存策略中使用的,可以让开发者的缓存策略更加灵活,workbox 内置了一些插件:

  • workbox.backgroundSync.Plugin: 如果网络请求失败,就将请求添加到 background sync 队列中,并且在下一个 sync 事件触发之前重新发起请求。

  • workbox.broadcastUpdate.Plugin: 当 Cache 缓存更新的时候将会广播一个消息给所有客户端,类似于 [sw-register-webpack-plugin](() 做的事情。

  • workbox.cacheableResponse.Plugin: 让匹配的请求的符合开发者指定的条件的返回结果可以被缓存,可以通过 statusheaders 参数指定一些规则条件。

  • workbox.expiration.Plugin: 管理 Cache 的数量以及 Cache 的时间长短。

可以像如下方式来使用 workbox 插件,以 workbox.expiration.Plugin 为例:

1

2

3

4

5

6

7

8

9

10

11

12

workbox.routing.registerRoute(

/.(?:png|gif|jpg|jpeg|svg)$/,

workbox.strategies.cacheFirst({

cacheName: ‘images’,

plugins: [

new workbox.expiration.Plugin({

maxEntries: 60, // 最大的缓存数,超过之后则走 LRU 策略清除最老最少使用缓存

maxAgeSeconds: 30 * 24 * 60 * 60, // 这只最长缓存时间为 30 天

}),

],

}),

);

自定义插件

当然,workbox 也知道这些插件肯定不能满足大家的自定义策略的要求,所以索性将整个请求生命周期中的关键事件以事件钩子回调函数的方式暴露出来,也就是说,我们可以使用这些事件钩子打造自己更加灵活的 workbox 插件。一个插件就是一个构造函数,而钩子就是以构造函数的方法的形式供开发者们自定义开发,下面介绍一下有哪些事件钩子:

cacheWillUpdate

cacheWillUpdate({request, response}),在请求返回结果替换 Cache 结果之前被调用,你可以在这个事件钩子中在更新缓存之前修改返回的结果,如果将结果直接设为 null 来避免当前请求的返回的结果被更新到缓存。

cacheDidUpdate

cacheDidUpdate({cacheName, request, oldResponse, newResponse}),当有新的缓存写入记录或者 Cache 缓存被更新时被调用,你可以调用这个方法在 Cache 缓存更新之后干点什么。

cachedResponseWillBeUsed

cachedResponseWillBeUsed({cacheName, request, matchOptions, cachedResponse}),在 Cache 缓存的结果在 fetch 事件中被触发响应返回之前调用,可以使用这个回调来允许或阻止正在使用的 Cache 响应返回。

requestWillFetch

requestWillFetch({request}),当任何 fetch 事件被触发的时候都会被调用,可以在这个毁掉中修改请求的 request 内容。

fetchDidFail

fetchDidFail({originalRequest, request}),当 fetch 事件触发失败的时候被调用,fetch 事件触发失败是网络根本无法请求,而不是请求返回为 非 200 的状态的时候,可以用这个钩子在检测到断网的时候干点什么。

如果你想写一个自己的 workbox 插件,只需要参照如下方式:

肯定不能满足大家的自定义策略的要求,所以索性将整个请求生命周期中的关键事件以事件钩子回调函数的方式暴露出来,也就是说,我们可以使用这些事件钩子打造自己更加灵活的 workbox 插件。一个插件就是一个构造函数,而钩子就是以构造函数的方法的形式供开发者们自定义开发,下面介绍一下有哪些事件钩子:

cacheWillUpdate

cacheWillUpdate({request, response}),在请求返回结果替换 Cache 结果之前被调用,你可以在这个事件钩子中在更新缓存之前修改返回的结果,如果将结果直接设为 null 来避免当前请求的返回的结果被更新到缓存。

cacheDidUpdate

cacheDidUpdate({cacheName, request, oldResponse, newResponse}),当有新的缓存写入记录或者 Cache 缓存被更新时被调用,你可以调用这个方法在 Cache 缓存更新之后干点什么。

cachedResponseWillBeUsed

cachedResponseWillBeUsed({cacheName, request, matchOptions, cachedResponse}),在 Cache 缓存的结果在 fetch 事件中被触发响应返回之前调用,可以使用这个回调来允许或阻止正在使用的 Cache 响应返回。

requestWillFetch

requestWillFetch({request}),当任何 fetch 事件被触发的时候都会被调用,可以在这个毁掉中修改请求的 request 内容。

fetchDidFail

fetchDidFail({originalRequest, request}),当 fetch 事件触发失败的时候被调用,fetch 事件触发失败是网络根本无法请求,而不是请求返回为 非 200 的状态的时候,可以用这个钩子在检测到断网的时候干点什么。

如果你想写一个自己的 workbox 插件,只需要参照如下方式:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值