webworker有如下三種:javascript
本文主要是對Service workers進行了講解。css
這是 JavaScript 工做原理的第八章。前端
或許你已經瞭解到漸進式網絡應用將只會愈來愈流行,由於它旨在創造擁有更加流暢的用戶體驗的網絡應用和建立類 App 的原生應用體驗而非瀏覽器端那樣的外觀和體驗。java
構建漸進式網絡應用的主要需求之一即在各類網絡和數據加載的條件下仍然可用-它能夠在網絡不穩定或者沒有網絡的狀況下使用。git
本文咱們將會深刻了解 Service Workers:它們是如何工做的以及你所應該關切的方面。github
若想理解 Service Workers 相關的一切,你首先應該閱讀一下以前發佈的有關 Web Workers 的文章。web
大致上,Service Worker 是一種 Web Worker,更準確地說,它更像是一個 Shared Worker。數據庫
Service Worker 接口之因此讓人感到興奮的緣由之一即它支持網絡應用離線運行,這使得開發者可以徹底控制網絡應用的行爲。promise
Service Worker 的生命週期和網頁徹底不相關。它由如下幾個步驟組成:瀏覽器
這發生於瀏覽器下載包含 Service Worker 相關代碼的 .js
文件。
爲了在網絡應用中使用 Service Worker,首先得在 JavaScript 代碼中對其進行註冊。當 Service Worker 註冊的時候,它會讓瀏覽器在後臺開始安裝 Service Worker 的步驟。
經過註冊 Service Worker,瀏覽器知曉包含 Service Worker 相關代碼的 JavaScript 文件。看下以下代碼:
if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('/sw.js').then(function(registration) { // 註冊成功 console.log('ServiceWorker registration successful'); }, function(err) { // 註冊失敗 console.log('ServiceWorker registration failed: ', err); }); }); }
以上代碼首先檢查當前執行環境是否支持 Service Worker API。若是是,則註冊 /sw.js
Service Worker。
能夠在每次頁面加載的時候,任意調用 register()
-瀏覽器會檢測 service worker
是否已經註冊從而進行適當地處理。
register()
方法裏面須要特別注意的地方即 Service Worker 文件地址。當前示例是在服務器根目錄下。意即 service worker 會做用於整個源地址上。換句話說,即 service worker 會接收到該域名下全部頁面 的 fetch
事件。若是註冊 service worker 的文件路徑是 /example/sw.js
,那麼 service worker 會接收到全部頁面路徑以 /example/
爲開頭的 URL 地址的 fetch
事件(好比 /example/page1/
/example/page2/
)。
在安裝階段,最好加載和緩存一些靜態資源。一旦靜態資源緩存成功,Service Worker 的安裝也就完成了。假若加載失敗-Service Worker 將會重試。一旦安裝成功,靜態資源也就緩存成功了。
這也就回答了爲何要在 load 事件以後註冊 Service Worker。這不是必須的,可是強烈推薦這麼作。
爲何要這樣作呢?假設用戶第一次訪問網絡應用。如今尚未註冊 service worker,並且瀏覽器沒法事先知曉是否會最終安裝它。若是進行安裝,則瀏覽器將會爲增長的線程開闢額外的 CPU 和內存,而這些資源本來是用來渲染網頁的。
參考下這裏,load
事件會加載完全部的資源好比圖片,樣式以後觸發。
最終的結果便是若是在頁面中安裝 Service Worker,將有可能致使頁面延遲加載和渲染-不可以讓用戶儘快地訪問網頁。
須要注意的是這隻會發生在第一次訪問頁面的時候。後續的頁面訪問不會被 Service Worker 的安裝所影響。一旦在首次訪問頁面的時候激活了 Service Worker ,它就能夠處理後續的頁面訪問所觸發的頁面加載/緩存事件。這是正確的,Service Worker 須要加載好以處理有限的網絡帶寬。
安裝完以後下一步即激活。該步驟是操做以前緩存資源的絕佳時機。
一旦激活,Service Worker 就能夠開始控制在其做用域內的全部頁面。一個有趣的事實即:註冊了 Service Worker 的頁面直到再次加載的時候纔會被 Service Worker 進行處理。當 Service Worker 開始進行控制,它有如下幾種狀態:
如下即其生命週期:
在頁面運行註冊 Service Worker 的過程當中,讓咱們來看看 Service Worker 腳本中發生的事情,它監聽 Service Worker 實例的 install
事件。
如下爲處理 install
事件所須要執行的步驟:
如下爲一個 Service Worker 內部可能的簡單安裝代碼:
var CACHE_NAME = 'my-web-app-cache'; var urlsToCache = [ '/', '/styles/main.css', '/scripts/app.js', '/scripts/lib.js' ]; self.addEventListener('install', function(event) { // event.waitUntil 使用 promise 來得到安裝時長及安裝是否失敗 event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); }) ); });
若是文件都成功緩存,則 service worker 安裝成功。若是任意文件下載失敗,那麼 service worker 將會安裝失敗。所以,請注意須要緩存的文件。
處理 install
事件徹底是可選,當不進行處理的時候,跳過以上幾個步驟便可。
該部分纔是乾貨。在這裏能夠看到如何攔截請求而後返回已建立的緩存(以及建立新的緩存)。
當 Service Worker 安裝完成以後,用戶會導航到另外一個頁面或者刷新當前頁面,Service Worker 將會收到 fetch 事件。這裏有一個演示瞭如何返回緩存的靜態資源或執行一個新的請求並緩存返回結果的過程的示例:
self.addEventListener('fetch', function(event) { event.respondWith( // 該方法查詢請求而後返回 Service Worker 建立的任何緩存數據。 caches.match(event.request) .then(function(response) { // 如有緩存,則返回 if (response) { return response; } // 複製請求。請求是一個流且只能被使用一次。由於以前已經經過緩存使用過一次了,因此爲了在瀏覽器中使用 fetch,須要複製下該請求。 var fetchRequest = event.request.clone(); // 沒有找到緩存。因此咱們須要執行 fetch 以發起請求並返回請求數據。 return fetch(fetchRequest).then( function(response) { // 檢測返回數據是否有效 if(!response || response.status !== 200 || response.type !== 'basic') { return response; } // 複製返回數據,由於它也是流。由於咱們想要瀏覽器和緩存同樣使用返回數據,因此必須複製它。這樣就有兩個流 var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { // 把請求添加到緩存中以備以後的查詢用 cache.put(event.request, responseToCache); }); return response; } ); }) ); });
大概的流程以下:
event.respondWith()
會決定如何響應 fetch
事件。 caches.match()
查詢請求而後返回以前建立的緩存中的任意緩存數據並返回 promise。fetch
。200
。同時檢查響應類型是否爲 basic,即檢查請求是否同域。當前場景不緩存第三方資源的請求。由於請求和響應都是流而流數據只能被使用一次,因此必須進行復制。並且因爲緩存和瀏覽器都須要使用它們,因此必須進行復制。
當用戶訪問網絡應用的時候,瀏覽器會在後臺試圖從新下載包含 Service Worker 代碼的 .js
文件。
若是下載下來的文件和當前的 Service Worker 代碼文件有一丁點兒不一樣,瀏覽器會認爲文件發生了改變而且會建立一個新的 Service Worker。
建立新的 Service Worker 的過程將會啓動,而後觸發 install
事件。然而,這時候,舊的 Service Worker 仍然控制着網絡應用的頁面意即新的 Service Worker 將會處於 waiting
狀態。
一旦關閉網絡應用當前打開的頁面,舊的 Service Worker 將會被瀏覽器殺死而新的 Service Worker 就能夠上位了。這時候將會觸發 activate
事件。
爲何全部這一切是必須的呢?這是爲了不在不一樣選項卡中同時運行不一樣版本的的網絡應用所形成的問題-一些在網頁中實際存在的問題且有可能會產生新的 bug(好比當在瀏覽器中本地存儲數據的時候卻擁有不一樣的數據庫結構)。
activate
回調中最爲常見的步驟即緩存管理。由於若想刪除安裝步驟中老舊的緩存,而這又會致使 Service Workers 沒法獲取該緩存中的文件數據,因此,這時候須要進行緩存管理。
這裏有一個示例演示如何把未在白名單中的緩存刪除(該狀況下,以 page-1
或者 page-2
來進行命名):
self.addEventListener('activate', function(event) { var cacheWhitelist = ['page-1', 'page-2']; event.waitUntil( // 得到緩存中全部鍵 caches.keys().then(function(cacheNames) { return Promise.all( // 遍歷全部的緩存文件 cacheNames.map(function(cacheName) { // 若緩存文件不在白名單中,刪除之 if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) ); });
當處於開發階段的時候,能夠經過 localhost 來使用 Service Workers ,但當處於發佈環境的時候,必須部署好 HTTPS(這也是使用 HTTPS 的最後一個緣由了)。
能夠利用 Service Worker劫持網絡鏈接和僞造響應數據。若是不使用 HTTPS,網絡應用會容易遭受中間人 攻擊。
爲了保證安全,必須經過 HTTPS 在頁面上註冊 Service Workers,這樣就能夠保證瀏覽器接收到的 Service Worker 沒有在傳輸過程當中被篡改。
Service Workers 擁有良好的瀏覽器兼容性。
你能夠追蹤全部瀏覽器的支持進程:
https://jakearchibald.github.io/isserviceworkerready/
Service Worker 獨有的功能:
這裏提到的每一個功能將會該系列以後的文章中進行詳細闡述。
因爲數據是由前端抓取的,這個時候就可使用 Service Workers 來處理相似播放器重載和再次流傳輸全部數據的情形。處理緩慢的網絡鏈接也是很是重要的。
參見維基百科關於流的定義,能夠更好地理解這裏的流的概念。