前言
餘為前端菜鳥,感姿勢水平匱乏,難觀前端之大局。遂決定循前端知識之脈絡,以興趣為引,輔以幾分堅持,望於己能解惑致知、於同道能助力一二,豈不美哉。
本系列程式碼及文件均在 此處
簡介
service worker是一個由來已久的HTML5 API,旨在建立持久的離線快取。類似於web worker,都是在瀏覽器後臺單獨開的一個執行緒內工作,可以向客戶端推訊息,不可操作dom。
service worker有自己的worker context,可以攔截請求和返回,快取檔案,必須在HTTPS或者本地環境下執行,非同步實現,內部大部分通過Promise實現
依賴Cache API,依賴FTML5 fetch API,依賴Promise實現離線快取功能
生命週期
parsed -> installing -> installed -> activating -> activated -> redundant
註冊
註冊在主執行緒中進行
if (navigator.serviceWorker) {
// register非同步方法
navigator.serviceWorker.register('./sw.js', { scope: '/' }).then(() => {
console.log('sw service worker 註冊成功')
console.log('parsed ----> installing')
}).catch((err) => {
console.log(err)
})
}
複製程式碼
- 註冊時的scope可選,預設是sw.js所在目錄,表示該worker可以接收該目錄下的所有fetch事件
- 每次呼叫egister會判斷worker是否已註冊再進行處理
- 註冊成功的worker有了自己的worker context,此時表示worker的指令碼被成功解析,轉入installing狀態
安裝
在worker的指令碼檔案中監聽install事件,可以維護初始快取列表
self.addEventListener('install', (event) => {
// 確保安裝完成前完成下面的操作
event.waitUntil(
// 建立一個cache
caches.open('v1').then((cache) => {
// cache是快取例項
// 呼叫該例項的addAll方法提前載入相關檔案進快取
return cache.addAll([
'/html/test.js',
'/html/default.html'
])
})
console.log('installing ----> installed ----> activating')
)
// self.skipWaiting() 直接進入activate
})
複製程式碼
啟用
worker安裝完成後會轉為installed/activating狀態,在滿足以下條件之一時可以轉為activate狀態
- 沒有active worker在執行或者舊的worker被釋放(頁面關閉)
- 呼叫self.skipWaiting()跳過waiting,進入activate狀態
activated狀態會觸發activate回撥,在worker監本中監聽activate事件
self.addEventListener('activate', function (event) {
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.filter(function (cacheName) {
return cacheName != 'v1';
}).map(function (cacheName) {
// 清除舊的快取
return caches.delete(cacheName);
})
)
})
console.log('activating ----> activated')
)
// self.clients.claim() 獲取頁面控制權,舊的worker失效
})
複製程式碼
啟用後的控制
啟用後可以控制頁面行為,可以處理功能事件,如fetch
, push
, message
符合worker的scope內的資源請求都會觸發fetch被控制
self.addEventListener('fetch', function (event) {
// 利用respondWith劫持響應
event.respondWith(
caches.open('v1').then(function (cache) {
return cache.match(event.request).then(function (response) {
// match到則返回否則直接請求
return response || fetch(event.request).then(function (response) {
// 404拋錯到catch處理
if (response.status === 404) {
throw new Error('nothing')
}
// 將response作為value存入cache
// install時可以進行快取
// 劫持fetch時也可以進行動態資源快取
cache.put(event.request, response.clone());
return response;
}).catch((err) => {
// 返回cache storage裡存的預設頁面
return caches.match('/html/default.html');
});
});
})
);
});
複製程式碼
localStorage是同步的,所以不能用於service worker內的儲存,IndexedDB可以用
廢棄狀態
安裝失敗、啟用失敗、被新的worker取代時轉入廢棄狀態
chrome中檢視
更新
當service worker指令碼內容更新時,會安裝新的檔案並觸發install,轉入installed/waiting狀態。此時舊的worker仍處於啟用狀態,在頁面關閉後會被廢棄,此後新開頁面裡新的worker才會生效
上圖為更改sw.js
後重新整理頁面的結果,關閉頁面重開後1497將是啟用中的worker
強制更新和檢查更新
一般為24小時
自動更新worker
// 跳過等待,直接進入activate
self.addEventListener('install', function (event) {
event.waitUntil(self.skipWaiting());
});
// actived之前更新客戶端
self.addEventListener('activate', function (event) {
event.waitUntil(
Promise.all([
// 更新客戶端所有的service worker
self.clients.claim(),
// 清理舊版本
caches.keys().then(function (cacheList) {
return Promise.all(
cacheList.map(function (cacheName) {
if (cacheName !== 'v1') {
return caches.delete(cacheName);
}
})
)
})
])
)
)}
複製程式碼
手動更新
主執行緒內每次註冊時進行更新
var version = '1.0';
navigator.serviceWorker.register('./sw.js').then(function (reg) {
if (localStorage.getItem('sw_version') !== version) {
// reg.update
reg.update().then(function () {
localStorage.setItem('sw_version', version)
});
}
});
複製程式碼
debug時更新
self.addEventListener('install', function () {
if (ENV === 'development') {
// 每次重新整理頁面重新註冊安裝時直接進入activate,確保最新
self.skipWaiting();
}
});
複製程式碼
cacheStorage
cacheStorage是在serviceworker規範中定義的介面,我們可以使用全域性的caches訪問cacheStorage
caches常用api有: open
, match
, delete
, has
, keys
例項
程式碼位於github
steps
-
本地利用
koa-static
建一個靜態頁面的serverconst Koa = require('koa') const path = require('path') const static = require('koa-static') const app = new Koa() const staticPath = './frontend/basic' app.use(static( path.join(__dirname, staticPath) )) 複製程式碼
-
頁面中插入指令碼
window.addEventListener('load', function () { if (navigator.serviceWorker) { navigator.serviceWorker.register('./sw.js').then(() => { console.log('sw service worker 註冊成功') }).catch((err) => { console.log(err) }) } }) 複製程式碼
-
sw.js
見 github
最終效果
-
offline模式下,可以從cache中讀取檔案快取
-
不存在的資源路徑返回快取好的
default.html
-
二次訪問快取list內的資源時劫持請求和響應,返回快取內容
雖發表於此,卻畢竟為一人之言,又是每日學有所得之筆記,內容未必詳實,看官老爺們還望海涵。