Service Worker
是HTML5 的一個新特性,主要用來作持久的離線緩存。javascript
在公司業務中,由於常常要處理性能優化方面的需求,使用傳統的性能優化手段,知足了大多數業務場景。可是,若是目標用戶手機性能以及網絡廣泛較差的狀況下(例如東南亞、印度等海外市場),瓶頸就在於
DNS查詢
,TCP
的創建時間,採用常規的優化手段就顯得捉襟見肘。此時,咱們項目組有嘗試採用離線緩存方案,即將靜態資源緩存到本地,經過攔截代理請求,讀取本地文件,加快訪問速度。html
這個 API 的惟一目的就是解放主線程,
Web Worker
是脫離在主線程以外的,將一些複雜的耗時的活交給它幹,完成後經過postMessage
方法告訴主線程,而主線程經過onMessage
方法獲得Web Worker
的結果反饋。java
Service Worker
擁有本身獨立的 worker
線程,獨立於當前網頁線程postMessage
向主線程發送消息Promise
異步實現Service Worker
安裝(installing
)完成後,就會一直存在,除非手動卸載(unregister
)
Service Worker
的生命週期徹底獨立於網頁webpack
register
)install
)activate
)一般使用
service worker
只須要如下幾個步驟:git
serivceworker
首先,檢測當前環境是否支持 service worker
,可使用 'serviceWorker' in navigator
進行檢測。github
若是支持,可使用 navigator.serviceWorker.register('./sw.js')
,在當前主線程中註冊 service worker
。若是註冊成功,service worker
則在 ServiceWorkerGlobalScope
環境中運行; 須要注意的是: 當前環境沒法操做DOM
,且和主線程之間相互獨立(即線程之間不會相互阻塞)。web
而後,後臺開始安裝service worker
,通常在此過程當中,開始緩存一些靜態資源文件。瀏覽器
安裝成功以後,準備進行激活 service worker
,一般在激活狀態下,主要進行緩存清理,更新service worker
等操做。緩存
激活成功後,,service worker
就能夠控制當前頁面了。須要注意的是,只有在service worker
成功激活後,才具備控制頁面的能力,通常在第一次訪問頁面時,service worker
第一次建立成功,並無激活,只有當刷新頁面,再次訪問以後,才具備控制頁面的能力。安全
本項目在第一次安裝serverworker
以後,能夠在控制檯看到如下信息:
查看對應請求的靜態資源信息:
能夠看到,第一次安裝
serviceworker
時,讀取到的靜態資源並無緩存。
能夠看到,靜態資源以及被
serviceworker
緩存起來了。
咱們再來查看當前serviceworker
安裝狀況:
能夠看到,
serviceworker
已經處於激活狀態。
最後,看一下離線功能的效果:
是否是很神奇,在離線狀態下咱們的頁面也是可以展現出數據的。 其中,離線的原理就是利用了
serviceworker
中,fetch
和cacheStorage
這兩個接口,將請求進行攔截,將響應進行緩存
該源碼實現瞭如下幾個功能:
self.skipWaiting()
,若是檢測到新的service worker
文件,就會當即替換掉舊的。cache.addAll(cacheFiles)
經過這個接口實現fetch
事件,能夠攔截當前頁全部請求self.addEventListener('fetch',function(e){})
cache.put(evt.request, response)
// 緩存靜態資源文件列表
let cacheFiles = [
'./test.js',
'./index.html',
'./src/img/yy.png'
]
// serviceworker使用版本
let __version__ = 'cache-v2'
// 緩存靜態資源
self.addEventListener('install', function (evt) {
// 強制更新sw.js
self.skipWaiting()
evt.waitUntil(
caches.open(version).then(function (cache) {
return cache.addAll(cacheFiles)
})
)
})
// 緩存更新
self.addEventListener('active', function (evt) {
evt.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.map(function (cacheName) {
if (cacheName !== version) {
return caches.delete(cacheName)
}
})
)
})
)
})
// 請求攔截
self.addEventListener('fetch', function (evt) {
console.log('處理fetch事件:', evt.request.url)
evt.respondWith(
caches.match(evt.request).then(function (response) {
if (response) {
console.log('緩存匹配到res:', response)
return response
}
console.log('緩存未匹配對應request,準備從network獲取', caches)
return fetch(evt.request).then(function (response) {
console.log('fetch獲取到的response:', response)
caches.open(version).then(function (cache) {
cache.put(evt.request, response)
return response
})
})
}).catch(function (err) {
console.error('fetch 接口錯誤', err)
throw err
})
)
})
複製代碼
請參考: 源碼地址