介紹
Service workers 本質(zhì)上充當(dāng)Web應(yīng)用程序與瀏覽器之間的代理服務(wù)器且叁,也可以在網(wǎng)絡(luò)可用時作為瀏覽器和網(wǎng)絡(luò)間的代理槽驶。它們旨在(除其他之外)使得能夠創(chuàng)建有效的離線體驗泊窘,攔截網(wǎng)絡(luò)請求并基于網(wǎng)絡(luò)是否可用以及更新的資源是否駐留在服務(wù)器上來采取適當(dāng)?shù)膭幼鳌K麄冞€允許訪問推送通知和后臺同步API若锁。
Service worker是一個注冊在指定源和路徑下的事件驅(qū)動worker泽台。它采用JavaScript控制關(guān)聯(lián)的頁面或者網(wǎng)站,攔截并修改訪問和資源請求访娶,細(xì)粒度地緩存資源商虐。你可以完全控制應(yīng)用在特定情形(最常見的情形是網(wǎng)絡(luò)不可用)下的表現(xiàn)。
Service worker運行在worker上下文,因此它不能訪問DOM秘车。相對于驅(qū)動應(yīng)用的主JavaScript線程典勇,它運行在其他線程中,所以不會造成阻塞叮趴。它設(shè)計為完全異步割笙,同步API(如XHR和localStorage不能在service worker中使用。
出于安全考量眯亦,Service workers只能由HTTPS承載伤溉,畢竟修改網(wǎng)絡(luò)請求的能力暴露給中間人攻擊會非常危險。在Firefox瀏覽器的用戶隱私模式妻率,Service Worker不可用乱顾。
Service workers之所以優(yōu)于以前同類嘗試(如AppCache),是因為它們無法支持當(dāng)操作出錯時終止操作宫静。Service workers可以更細(xì)致地控制每一件事情走净。
Service workers大量使用Promise,因為通常它們會等待響應(yīng)后繼續(xù)孤里,并根據(jù)響應(yīng)返回一個成功或者失敗的操作伏伯。Promise非常適合這種場景。
根據(jù)官方的介紹不難看出扭粱,Service worker提供了更有效的緩存請求資源控制手段舵鳞。
基本使用
使用ServiceWorkerContainer.register()
注冊service worker,這將作用于整個域內(nèi)用戶可訪問的URL琢蛤,或者其特定子集蜓堕。
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
}
taobaofed為什么說 SW是網(wǎng)站的大腦?舉個例子博其,如果在 www.example.com
的根路徑下注冊了一個 SW套才,那么這個 SW 將可以控制所有該瀏覽器向 www.example.com
站點發(fā)起的請求。只需要監(jiān)聽 fetch 事件慕淡,你就可以任意的操縱請求背伴,可以返回從 CacheStorage 中讀的數(shù)據(jù),也可以通過 Fetch API 發(fā)起新的請求峰髓,甚至可以 new 一個 Response傻寂,返回給頁面。
直接看一個完整的sw文件示例去體會用法:
var cacheStorageKey = 'cachesName';
var cacheList = [
// 注冊成功后要立即緩存的資源列表
]
// 當(dāng)瀏覽器解析完 SW 文件時觸發(fā) install 事件
self.addEventListener('install', function(e) {
// install 事件中一般會將 cacheList 中要緩存的內(nèi)容通過 addAll 方法携兵,
// 請求一遍放入 caches 中
e.waitUntil(
caches.open(cacheStorageKey).then(function(cache) {
return cache.addAll(cacheList)
})
);
});
// 激活時觸發(fā) activate 事件
self.addEventListener('activate', function(e) {
// active 事件中通常做一些過期資源釋放的工作疾掰,匹配到就從 caches 中刪除
var cacheDeletePromises = caches.keys().then(cacheNames => {
return Promise.all(cacheNames.map(name => {
if (name !== cacheStorageKey) {
return caches.delete(name);
} else {
return Promise.resolve();
}
}));
});
e.waitUntil(
Promise.all([cacheDeletePromises])
);
});
self.addEventListener('fetch', function(e) {
// 在此編寫緩存策略
e.respondWith(
// 可以通過匹配緩存中的資源返回
caches.match(e.request)
// 也可以從遠(yuǎn)端拉取
fetch(e.request.url)
// 也可以自己造
new Response('自己造')
// 也可以通過吧 fetch 拿到的響應(yīng)通過 caches.put 方法放進(jìn) caches
);
});
監(jiān)聽install事件,在解析完后寫入需要緩存的cache
監(jiān)聽activate事件徐紧,觸發(fā)時可以清理舊緩存和舊的service worker關(guān)聯(lián)的東西静檬。
監(jiān)聽fetch事件炭懊,去響應(yīng)請求,通過respondWith去任意修改對于這些請求的響應(yīng)拂檩。
示例也基本符合了MDN官方給出的使用建議侮腹。
Workbox 3
workbox被定義為 PWA 相關(guān)的工具集合,可以把它理解為 Google 官方的 PWA 框架稻励,它解決的就是用底層 API 寫 PWA 太過復(fù)雜的問題父阻,讓管理service worker更加簡單。
直接來看taobaofed給出的案例
// 首先引入 Workbox 框架
importScripts('[https://storage.googleapis.com/workbox-cdn/releases/3.3.0/workbox-sw.js](https://storage.googleapis.com/workbox-cdn/releases/3.3.0/workbox-sw.js)');
workbox.precaching([
// 注冊成功后要立即緩存的資源列表
]);
// html的緩存策略
workbox.routing.registerRoute(
new RegExp(''.*\.html'),
workbox.strategies.networkFirst()
);
workbox.routing.registerRoute(
new RegExp('.*\.(?:js|css)'),
workbox.strategies.cacheFirst()
);
workbox.routing.registerRoute(
new RegExp('https://your\.cdn\.com/'),
workbox.strategies.staleWhileRevalidate()
);
workbox.routing.registerRoute(
new RegExp('[https://your\.img\.cdn\.com/](https://your/.img/.cdn/.com/)'),
workbox.strategies.cacheFirst({
cacheName: 'example:img'
})
);
對比原先就比較清晰明了望抽。workbox.precaching
即install時塞進(jìn)caches的內(nèi)容至非。workbox.routing.registerRoute
去匹配請求路徑,匹配上了走相應(yīng)的請求策略糠聪,等于activate和fetch方法都合并在了一個方法里去配置,更加獨立簡介明了谐鼎。
緩存策略
- Stale-While-Revalidate 當(dāng)請求的路由有對應(yīng)的 Cache 緩存結(jié)果就直接返回舰蟆,在返回 Cache 緩存結(jié)果的同時會在后臺發(fā)起網(wǎng)絡(luò)請求拿到請求結(jié)果并更新 Cache 緩存,如果本來就沒有 Cache 緩存的話狸棍,直接就發(fā)起網(wǎng)絡(luò)請求并返回結(jié)果身害,這對用戶來說是一種非常安全的策略,能保證用戶最快速的拿到請求的結(jié)果草戈,但是也有一定的缺點塌鸯,就是還是會有網(wǎng)絡(luò)請求占用了用戶的網(wǎng)絡(luò)帶寬。
- Cache First (Cache Falling Back to Network) 當(dāng)匹配到請求之后直接從 Cache 緩存中取得結(jié)果唐片,如果 Cache 緩存中沒有結(jié)果丙猬,那就會發(fā)起網(wǎng)絡(luò)請求,拿到網(wǎng)絡(luò)請求結(jié)果并將結(jié)果更新至 Cache 緩存费韭,并將結(jié)果返回給客戶端茧球。這種策略比較適合結(jié)果不怎么變動且對實時性要求不高的請求。
- Network First (Network Falling Back to Cache) 請求路由是被匹配的星持,就采用網(wǎng)絡(luò)優(yōu)先的策略抢埋,也就是優(yōu)先嘗試拿到網(wǎng)絡(luò)請求的返回結(jié)果,如果拿到網(wǎng)絡(luò)請求的結(jié)果督暂,就將結(jié)果返回給客戶端并且寫入 Cache 緩存揪垄,如果網(wǎng)絡(luò)請求失敗,那最后被緩存的 Cache 緩存結(jié)果就會被返回到客戶端逻翁,這種策略一般適用于返回結(jié)果不太固定或?qū)崟r性有要求的請求饥努,為網(wǎng)絡(luò)請求失敗進(jìn)行兜底。
- Network Only 直接強(qiáng)制使用正常的網(wǎng)絡(luò)請求卢未,并將結(jié)果返回給客戶端肪凛,這種策略比較適合對實時性要求非常高的請求堰汉。
- Cache Only 直接使用 Cache 緩存的結(jié)果,并將結(jié)果返回給客戶端伟墙,這種策略比較適合一上線就不會變的靜態(tài)資源請求翘鸭。
總結(jié)心得
雖然MDN上指出IE、Safari目前并不支持戳葵,參考淘寶 PC 首頁的 Service Worker 上線已經(jīng)有好長一段時間了就乓,其實是可以考慮逐漸引入使用的。且因為通過官方提供的底層API去寫可能太過復(fù)雜拱烁,直接使用workbox是一個不錯的策略生蚁!
參考
Workbox 3:Service Worker 可以如此簡單
Service_Worker_API from MDN
workbox緩存策略