簡(jiǎn)介
service worker通過(guò)攔截fetch事件,處理網(wǎng)絡(luò)請(qǐng)求。使用promise異步處理,所以不會(huì)阻塞主進(jìn)程的執(zhí)行。常用于借助Cache API緩存資源。還可以使用Push API實(shí)現(xiàn)推送通知(Safari不支持)、Background Synchronization后臺(tái)同步(Safari、Firefox不支持)等功能。
dist vs memory vs sw
瀏覽器為了提升訪問(wèn)的速度,會(huì)將緩存資源分為內(nèi)存緩存memory cache與磁盤(pán)緩存dist cache,小資源在短時(shí)間內(nèi)會(huì)被放在內(nèi)存中,加快訪問(wèn)的速度。值得注意的是這個(gè)存放目前沒(méi)有標(biāo)準(zhǔn),所以不同瀏覽器存儲(chǔ)的策略有些不同。
在資源緩存后,再次訪問(wèn)資源,瀏覽器會(huì)先檢測(cè)內(nèi)存有無(wú)資源,再發(fā)起fetch請(qǐng)求。所以service worker無(wú)法攔截內(nèi)存中的資源,但可以處理磁盤(pán)上的資源。
service worker利用Cache API緩存資源到磁盤(pán)中。而瀏覽器在管理磁盤(pán)的使用時(shí),也有可能刪除已緩存的資源。
在service worker未啟動(dòng)或停止運(yùn)行時(shí),瀏覽器需要先啟動(dòng)service worker再請(qǐng)求,所以可能會(huì)因此延遲一點(diǎn)時(shí)間。
總結(jié):
優(yōu)先級(jí): memory > sw > dist
加載速度: memory > dist > sw
service-worker優(yōu)點(diǎn):
- 完全控制網(wǎng)絡(luò)請(qǐng)求,可以設(shè)置請(qǐng)求的響應(yīng)頭
- 限制緩存的資源數(shù)量
- 自定義過(guò)期策略
- 可以使用indexDB存儲(chǔ)數(shù)據(jù),使用postMessage傳遞信息
- Disabled Cache不會(huì)清除緩存
- 支持離線訪問(wèn)網(wǎng)站
service-worker缺點(diǎn):
- 無(wú)法訪問(wèn)DOM,無(wú)法使用同步方法,如localStorage
- 必須再https環(huán)境下使用
- 需要在作用范圍的根目錄下創(chuàng)建service-worker,如要將全局的頁(yè)面進(jìn)行管理,就需要在根目錄下注冊(cè)
- 速度比瀏覽器緩存慢
- 在存在memory cache的情況下,無(wú)法更新緩存
緩存功能
service worker通過(guò)Cache API進(jìn)行緩存時(shí),有兩種不同的緩存方式,一種是預(yù)緩存,另一種是運(yùn)行時(shí)緩存。
預(yù)緩存通常在service worker安裝階段進(jìn)行,通過(guò)預(yù)緩存可以提前加載資源。但需要注意加載資源的時(shí)機(jī)與數(shù)量。
運(yùn)行時(shí)緩存是用于緩存已訪問(wèn)的資源,優(yōu)化資源的再次獲取。
運(yùn)行時(shí)緩存策略
- Cache First 緩存優(yōu)先,無(wú)緩存請(qǐng)求網(wǎng)絡(luò)資源
- Network First 網(wǎng)絡(luò)優(yōu)先,無(wú)網(wǎng)絡(luò)使用緩存資源
- Network Only 只通過(guò)網(wǎng)絡(luò)獲取資源
- Cache Only 只通過(guò)緩存獲取資源,需配合預(yù)緩存
- Stale-While-Revalidate 緩存優(yōu)先,然后會(huì)發(fā)起請(qǐng)求更新緩存
實(shí)戰(zhàn)
service worker的生命周期為:注冊(cè)(register) -> 安裝(install) -> 激活(active)。在安裝后service work就會(huì)激活,只有激活后才會(huì)生效。
要注冊(cè)service worker需要提供service-worker.js,用于描述service worker的用途,注意service-worker.js文件的位置決定了service-worker控制的頁(yè)面,對(duì)于service-worker.js文件外部的頁(yè)面是無(wú)法控制的。注意文件的位置限制的是service-worker控制的頁(yè)面,而不是請(qǐng)求的地址,service-worker依然能控制從當(dāng)前頁(yè)面發(fā)送到外部包括跨域的請(qǐng)求。
此處不展開(kāi)描述原生service worker的配置方法。
為了方便使用,google推出了service-worker-workerbox簡(jiǎn)化使用,只需要簡(jiǎn)單的配置就可以自動(dòng)生成service-worker.js文件。
而對(duì)于注冊(cè)與安裝的命令也可以使用register-service-worker庫(kù)。
聚焦到具體的項(xiàng)目中。
目前項(xiàng)目中使用了umi的框架,所以需在umi的config文件中導(dǎo)入workbox-webpack-plugin庫(kù)。
import { defineConfig } from 'umi';
import { GenerateSW } from 'workbox-webpack-plugin';
export default defineConfig({
.....other config
chainWebpack(memo) {
// workbox 配置
memo.plugin('workbox').use(GenerateSW, [
{
clientsClaim: true, // Service Worker 被激活后使其立即獲得頁(yè)面控制權(quán)
cleanupOutdatedCaches: true, //刪除過(guò)時(shí)、老版本的緩存
include: ['**/*.{html,js,css,png.jpg}'], // 匹配的文件
exclude: ['service-wroker.js'], // 忽略的文件
runtimeCaching: [
{
urlPattern: /.*\.js.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'seed-js',
expiration: {
maxEntries: 20, //最多緩存20個(gè),超過(guò)的按照LRU原則刪除
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
},
},
},
{
urlPattern: /.*css.*/,
handler: 'CacheFirst',
options: {
cacheName: 'seed-css',
expiration: {
maxEntries: 30, //最多緩存30個(gè),超過(guò)的按照LRU原則刪除
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
},
},
},
{
urlPattern: /.*(png|svga).*/,
handler: 'CacheFirst',
options: {
cacheName: 'seed-image',
expiration: {
maxEntries: 30, //最多緩存30個(gè),超過(guò)的按照LRU原則刪除
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
},
},
},
],
},
]);
},
})
然后在app.ts中調(diào)用register-service-worker,注冊(cè)service worker。
import { register } from 'register-service-worker';
import { isBrowser } from 'umi';
if (process.env.NODE_ENV === 'production' && isBrowser()) {
register(`/service-worker.js`, {
ready(registration) {
console.log('Service worker is active.');
},
registered(registration) {
console.log('Service worker has been registered.');
},
cached(registration) {
console.log('Content has been cached for offline use.');
},
updatefound(registration) {
console.log('New content is downloading.');
},
updated(registration) {
console.log('New content is available; please refresh.');
},
offline() {
console.log(
'No internet connection found. App is running in offline mode.',
);
},
error(error) {
console.error('Error during service worker registration:', error);
},
});
}
這樣就完成了一個(gè)簡(jiǎn)單的service worker配置。
參考
stackoverflow-why-use-a-service-worker
ServiceWorker issue: Difference between disk and memory cache
Service worker caching and HTTP caching