——evilrescuer翻譯自Matt Gaunt
寫(xiě)的[Service Workers: an Introduction](收錄于 谷歌開(kāi)發(fā)者)(https://developers.google.com/web/fundamentals/primers/service-workers/)
上一篇:Service Workers簡(jiǎn)介(一)
一、緩存并返回請(qǐng)求
現(xiàn)在你已經(jīng)安裝了一個(gè)service worker,你可能想要返回一個(gè)自己緩存的響應(yīng),對(duì)吧些楣?
在你安裝完service worker,用戶導(dǎo)航到另一個(gè)頁(yè)面或者刷新頁(yè)面承绸,service worker 就開(kāi)始接收f(shuō)etch事件兜蠕,例子如下:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
這里唐片,我們定義了我們的fetch事件莱预,并在event.respondWith()里,我們從caches.match傳入了一個(gè)promise项滑。這個(gè)方法從任何你的service worker緩存的文件中查找結(jié)果依沮。
如果我們有一個(gè)匹配上的response,就返回緩存的值枪狂,否則就返回調(diào)用fetch的結(jié)果危喉,也就是發(fā)起網(wǎng)絡(luò)請(qǐng)求,并返回收到的數(shù)據(jù)州疾。
如果我們想累積緩存一個(gè)新的請(qǐng)求辜限,可以處理fetch請(qǐng)求的response并把這個(gè)response加入緩存,就像下面這樣:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request).then(
function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// IMPORTANT: Clone the response. A response is a stream
// and because we want the browser to consume the response
// as well as the cache consuming the response, we need
// to clone it so we have two streams.
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
我們做了這些:
- 在
fetch
請(qǐng)求上严蓖,給.then()
增加一個(gè) callback - 一旦我們收到一個(gè)響應(yīng), 我們做這些檢查:
- 確認(rèn)響應(yīng)是合法的.
- 檢查狀態(tài)是
200
. - 確認(rèn)響應(yīng)類型是 basic(說(shuō)明是從我們的源發(fā)起的請(qǐng)求)薄嫡,也就是不緩存第三方文件發(fā)起的請(qǐng)求的響應(yīng).
- 如果通過(guò)測(cè)試,我們 克隆 一個(gè)響應(yīng)颗胡。因?yàn)轫憫?yīng)是一個(gè) 流毫深,響應(yīng)體只會(huì)被消耗一次。因?yàn)槲覀冃枰o瀏覽器返回響應(yīng)毒姨,并且緩存起來(lái)以便將來(lái)使用, 所以我們需要克隆一下哑蔫,一個(gè)用來(lái)發(fā)給瀏覽器,一個(gè)用來(lái)緩存起來(lái)弧呐。
二闸迷、更新一個(gè)service worker
有時(shí)你的service worker需要更新,此時(shí)你需要:
- 更新你的 service worker JavaScript 文件. 當(dāng)用戶來(lái)到你的網(wǎng)頁(yè)俘枫,瀏覽器嘗試重新下載這個(gè)JavaScript 文件腥沽。這個(gè)文件有任何改變,哪怕是一個(gè)字節(jié)崩哩,都會(huì)被認(rèn)為是新的.
- 你的新 service worker 將會(huì)啟動(dòng)巡球,install的事件會(huì)被觸發(fā).
- 舊的service worker還在控制當(dāng)前頁(yè)面,所以新的service worker會(huì)進(jìn)入一個(gè)等待的狀態(tài)邓嘹。
- 當(dāng)前打開(kāi)的頁(yè)面如果被關(guān)閉酣栈,舊的 service worker會(huì)被銷毀,新的開(kāi)始控制汹押。
- 一旦新的service worker得到控制權(quán)矿筝,它的activate事件會(huì)被觸發(fā).
我們常常在activate的callback中處理緩存管理。注意:如果你在安裝步驟清掉舊緩存棚贾,那些仍然有控制權(quán)的舊service worker窖维,會(huì)立即停止那個(gè)緩存文件的工作榆综,所以在這里做比較好。
比如說(shuō)铸史,我們有一個(gè)緩存叫 'my-site-cache-v1'鼻疮,我們希望把這個(gè)緩存分割成兩個(gè),一個(gè)給頁(yè)面用琳轿,一個(gè)給博客文章用判沟。那在install(安裝)步驟,我們創(chuàng)建兩個(gè)緩存:'pages-cache-v1' 和 'blog-posts-cache-v1' 崭篡,然后在activate(激活)步驟挪哄,我們刪掉我們的舊緩存'my-site-cache-v1'。
下面的代碼會(huì)循環(huán)查看 service worker 的緩存琉闪,刪掉那些不在白名單上的緩存迹炼。
self.addEventListener('activate', function(event) {
var cacheWhitelist = ['pages-cache-v1', 'blog-posts-cache-v1'];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
三、粗糙的邊緣和陷阱
server worker是個(gè)新玩意兒颠毙。這里提一些問(wèn)題斯入,希望可以盡早被解決,但是現(xiàn)在還是需要注意一下吟秩。
1.安裝失敗的信息無(wú)法告知
如果你注冊(cè)了一個(gè)service worker咱扣,但是沒(méi)在chrome://inspect/#service-workers or chrome://serviceworker-internals中出現(xiàn),那可能是因?yàn)閽伋鲥e(cuò)誤涵防,或者一個(gè)可被傳輸?shù)絜vent.waitUntil()的rejected promise錯(cuò)誤
為了解決這個(gè)問(wèn)題闹伪,前往 chrome://serviceworker-internals
并打開(kāi)谷歌開(kāi)發(fā)工具,對(duì)安裝事件進(jìn)行調(diào)試壮池。 以及配合這篇文章提到的: 《調(diào)試未捕獲的異称浚》。
2.fetch的默認(rèn)情況
2.1 沒(méi)有credentials憑據(jù)
當(dāng)你使用fetch時(shí)椰憋,默認(rèn)情況下厅克,請(qǐng)求是不帶credentials憑據(jù)的(比如cookies),如果你需要憑據(jù)橙依,可以這樣使用:
fetch(url, {
credentials: 'include'
})
對(duì)于同源的URL來(lái)說(shuō)证舟,我們特意這么設(shè)置后, 會(huì)比 XHR復(fù)雜一些。fetch行為和其他的CORS 請(qǐng)求一樣, 比如 <img crossorigin>, 如果你不設(shè)置<img crossorigin="use-credentials">窗骑,它是不會(huì)發(fā)cookies的女责。
2.2 非CORS默認(rèn)安裝失敗
By default, fetching a resource from a third party URL will fail if it doesn't support CORS. You can add a no-CORS option to the Request to overcome this, although this will cause an 'opaque' response, which means you won't be able to tell if the response was successful or not.
默認(rèn)情況下,由于沒(méi)有CORS创译,從第三方URL獲取一個(gè)資源會(huì)失敗抵知。你可以加上一個(gè)no-CORS,盡管這會(huì)導(dǎo)致一些不透明的響應(yīng),因?yàn)槟悴恢理憫?yīng)是否成功刷喜。做法如下:
cache.addAll(urlsToPrefetch.map(function(urlToPrefetch) {
return new Request(urlToPrefetch, { mode: 'no-cors' });
})).then(function() {
console.log('All resources have been fetched and cached.');
});
譯者注:如果你不了解no-CORS残制,請(qǐng)查看 request模式-MDN
2.3 處理響應(yīng)式圖像
對(duì)于service worker來(lái)說(shuō),如果你想在安裝步驟緩存一張圖片掖疮,你有幾種選擇:
- 安裝所有可能用到的圖片
- 緩存低分辨率版本圖片
- 緩存高分辨率版本圖片
實(shí)際上你應(yīng)該選擇選項(xiàng)2或3初茶,因?yàn)橄螺d所有圖像會(huì)浪費(fèi)存儲(chǔ)空間。
假設(shè)您在安裝時(shí)選擇低分辨率版本浊闪,并且您希望在加載頁(yè)面時(shí)嘗試從網(wǎng)絡(luò)中檢索更高分辨率的圖像纺蛆,但如果高分辨率圖像失敗,則回退到低分辨率版本规揪。這看起來(lái)很好,但有一個(gè)問(wèn)題温峭。
比如我們有下面兩張圖片:
Screen Density Width Height
1x 400 400
2x 800 800
我們可能這么使用:
<img src="image-src.png" srcset="image-src.png 1x, image-2x.png 2x" />
如果我們?cè)?x的顯示器上猛铅,瀏覽器會(huì)去下載 image-2x.png,如果離線了凤藏,你可以用.catch() 緩存奸忽,并返回 image-src.png(如果已經(jīng)緩存上的話)。
但是瀏覽器會(huì)期望圖像考慮到2x屏幕上的額外像素揖庄,因此圖像將顯示為200x200 CSS像素而不是400x400 CSS像素栗菜。唯一的方法是在圖像上設(shè)置固定的高度和寬度。
四蹄梢、學(xué)習(xí)更多
這里有一系列關(guān)于 service worker 的文檔: https://jakearchibald.github.io/isserviceworkerready/resources 疙筹,可能對(duì)你有用。
(完)