一唐含、前言
近期在研究網(wǎng)頁(yè)緩存機(jī)制的時(shí)候?qū)W習(xí)了一下漸進(jìn)式 Web 應(yīng)用(PWA)中了 ServiceWorkers,并在 AWTK在線幫助文檔 中實(shí)踐應(yīng)用了一下秕硝,在此做個(gè)記錄芥映。
二、漸進(jìn)式 Web 應(yīng)用(PWA)
PWA全稱 Progressive Web App缝裤,即漸進(jìn)式Web應(yīng)用屏轰,實(shí)際上就是一個(gè)網(wǎng)頁(yè)應(yīng)用,加上App Manifest 和 ServiceWorker 來(lái)實(shí)現(xiàn) PWA 的安裝和離線訪問(wèn)等功能憋飞,使得Web應(yīng)用接近原生 APP霎苗。
- 可以添加到主屏幕,點(diǎn)擊主屏幕圖標(biāo)可以實(shí)現(xiàn)啟動(dòng)動(dòng)畫以及隱藏地址欄榛做;
- 實(shí)現(xiàn)離線緩存功能唁盏,即使客戶端沒(méi)有網(wǎng)絡(luò),依然可以正常訪問(wèn)检眯;
- 實(shí)現(xiàn)消息推送等等……
我們這里主要使用 Service Worker 實(shí)現(xiàn)離線緩存厘擂,減少網(wǎng)絡(luò)通信,提高頁(yè)面刷新效率锰瘸。
三刽严、ServiceWorker
ServiceWorker 是 Chrome 團(tuán)隊(duì)提出并力推的一個(gè) Web API,用于給 Web 應(yīng)用提供高級(jí)的可持續(xù)的后臺(tái)處理能力避凝。
它本質(zhì)上充當(dāng)服務(wù)器和瀏覽器之間的代理服務(wù)器舞萄,能夠攔截網(wǎng)站的請(qǐng)求,從而控制頁(yè)面加載管削。
在 Service Worker 中我們可以做很多事情倒脓,比如攔截客戶端的請(qǐng)求、向客戶端發(fā)送消息含思、向服務(wù)器發(fā)起請(qǐng)求等等崎弃,其中最重要的作用之一就是離線資源緩存。
由于其攔截請(qǐng)求的特性含潘,出于安全考慮饲做,Service Worker 只能被使用在 https 或者本地的 localhost 環(huán)境下。
關(guān)于 Service Worker 在不同瀏覽器的兼容性调鬓,詳見(jiàn) caniuse 中提供的信息:
可以看到 IE 和 Opera Mini 的全版本都不支持艇炎,但主流瀏覽器:Edge、Chrome腾窝、Firefox缀踪、Safari 較新的版本都是支持的居砖,可以放心用,不過(guò)建議在用之前還是得先做好評(píng)估驴娃。
四奏候、ServiceWorker啟動(dòng)過(guò)程
當(dāng)用戶首次導(dǎo)航至 URL 時(shí),服務(wù)器會(huì)返回響應(yīng)的網(wǎng)頁(yè)唇敞。
- 調(diào)用 register() 函數(shù)時(shí)蔗草, Service Worker 開(kāi)始下載。
- 在注冊(cè)過(guò)程中疆柔,瀏覽器會(huì)下載咒精、解析并執(zhí)行 ServiceWorker()。如果在此步驟中出現(xiàn)任何錯(cuò)誤旷档,register() 返回的 promise 都會(huì)執(zhí)行 reject 操作模叙,并且 Service Worker 會(huì)被廢棄。
- 一旦 ServiceWorker 成功執(zhí)行了鞋屈,install 事件就會(huì)激活范咨。
- 安裝完成,ServiceWorker 便會(huì)激活厂庇,并控制在其范圍內(nèi)的一切渠啊。如果生命周期中的所有事件都成功了,ServiceWorker 便已準(zhǔn)備就緒权旷,隨時(shí)可以使用了替蛉。
在 ServiceWorker 的啟動(dòng)過(guò)程中,若有任何環(huán)節(jié)出錯(cuò)拄氯,則 ServiceWorker 會(huì)被直接廢棄灭返,直到下次刷新頁(yè)面,將重新啟動(dòng)坤邪。
五、實(shí)現(xiàn)ServiceWorker
5.1 注冊(cè)
如果當(dāng)前使用的瀏覽器支持 ServiceWorker 罚缕,在 window.navigator 下則會(huì)存在 serviceWorker 對(duì)象艇纺,我們可以使用這個(gè)對(duì)象的 register 方法來(lái)注冊(cè)一個(gè) ServiceWorker。
備注:ServiceWorker在使用的過(guò)程中存在大量的 Promise 邮弹,對(duì)于 Promise 不是很了解的話可以先去看一下相關(guān)文檔黔衡。ServiceWorker 的注冊(cè)方法返回的也是一個(gè) Promise 。
/* index.html or index.js */
/* 判斷當(dāng)前瀏覽器是否支持serviceWorker */
if ('serviceWorker' in navigator) {
/* 當(dāng)頁(yè)面加載完成就創(chuàng)建一個(gè)serviceWorker */
window.addEventListener('load', function () {
/* register 方法接受兩個(gè)參數(shù) */
/* 參數(shù)一:指定serviceWorker文件路徑 */
/* 參數(shù)二:scope參數(shù)是可選的腌乡,可以用來(lái)指定你想讓serviceWorker控制的內(nèi)容子目錄 */
/* 在這個(gè)例子里盟劫,我們指定了'/',表示根網(wǎng)域下的所有內(nèi)容与纽。這也是默認(rèn)值侣签。 */
navigator.serviceWorker.register('./service-worker.js', {scope: './'})
.then(function (registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
})
.catch(function (err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
register 方法返回一個(gè) Promise 塘装。如果注冊(cè)失敗,可以通過(guò) catch 來(lái)捕獲錯(cuò)誤信息影所;如果注冊(cè)成功蹦肴,可以使用 then 來(lái)獲取一個(gè) ServiceWorkerRegistration 的實(shí)例。
5.2 安裝
注冊(cè)完 ServiceWorker 之后猴娩,瀏覽器會(huì)為我們自動(dòng)安裝它阴幌,因此我們就可以在 register 時(shí)指定的 service-worker.js 文件中編寫對(duì)應(yīng)的安裝及攔截邏輯,監(jiān)聽(tīng)它的 install 事件卷中。
/* service-worker.js */
this.addEventListener('install', function (event) {
console.log('Service Worker install');
});
類似的矛双,ServiceWorker 在安裝完成后會(huì)被激活,所以我們也可監(jiān)聽(tīng) activate 事件蟆豫。
/* service-worker.js */
this.addEventListener('activate', function (event) {
console.log('Service Worker activate');
});
這時(shí)议忽,我們可以在瀏覽器開(kāi)發(fā)者工具的”應(yīng)用程序“欄目中看到我們注冊(cè)的 ServiceWorker。
在默認(rèn)情況下无埃,ServiceWorker 必定會(huì)每24小時(shí)被下載一次徙瓶,如果下載的文件是最新文件,那么它就會(huì)被重新注冊(cè)和安裝嫉称,但不會(huì)被激活侦镇,當(dāng)不再有頁(yè)面使用舊的 ServiceWorker 的時(shí)候,它就會(huì)被激活织阅。
備注:由于以上特性不方便開(kāi)發(fā)壳繁,因此這里勾引選了"重新加載時(shí)更新"的選項(xiàng),勾選后每次刷新頁(yè)面都能使用最新的 ServiceWorker 文件荔棉。
此處編寫一個(gè)最簡(jiǎn)單的緩存 index.html 的例子闹炉,代碼如下:
/* service-worker.js */
/* 監(jiān)聽(tīng)安裝事件,install事件一般是被用來(lái)設(shè)置瀏覽器的離線緩存邏輯 */
this.addEventListener('install', function (event) {
/* 通過(guò)這個(gè)方法可以防止緩存未完成润樱,就關(guān)閉serviceWorker */
event.waitUntil(
/* 創(chuàng)建一個(gè)名叫V1的緩存版本 */
caches.open('v1').then(function (cache) {
/* 指定要緩存的內(nèi)容渣触,地址為相對(duì)于跟域名的訪問(wèn)路徑 */
return cache.addAll([
'./index.html'
]);
})
);
});
/* 注冊(cè)fetch事件,攔截全站的請(qǐng)求 */
this.addEventListener('fetch', function(event) {
event.respondWith(
/* magic goes here */
......
/* 在緩存中匹配對(duì)應(yīng)請(qǐng)求資源直接返回 */
caches.match(event.request)
);
});
六壹若,借助VuePess的插件實(shí)現(xiàn)ServiceWorker
VuePress 官方提供了對(duì)PWA的支持嗅钻,安裝 plugin-pwa 插件后,在 config.js 中進(jìn)行以下配置即可:
module.exports = {
/* 在HTML<head>標(biāo)簽中插入PWA配置文件 */
head: [
['link', { rel: 'manifest', href: '/manifest.json' }]
……
],
……
plugins: [
'@vuepress/pwa', { /* 啟用PWA插件店展,實(shí)現(xiàn)ServiceWorker緩存機(jī)制 */
serviceWorker: true,/* VuePress將自動(dòng)生成并注冊(cè)一個(gè)ServiceWorker */
updatePopup: true /* 監(jiān)聽(tīng)服務(wù)端內(nèi)容养篓,發(fā)生更新時(shí)提供refersh按鈕刷新頁(yè)面 */
},
]
}
配置時(shí)需要提供 Manifest 和 icons,具體詳見(jiàn):Web app manifests赂蕴。