您可能已經知道,漸進式Web應用(PWA)會愈來愈受歡迎,由於它們旨在使Web應用的用戶體驗更加流暢,建立Native應用程序般的體驗,而不僅是運行在瀏覽器的應用。css
構建漸進式Web應用程序的主要緣由之一是使應用在網絡和加載方面很是可靠 - 它應該可用於不肯定或不存在的網絡條件。web
在這篇文章中,咱們將深刻探討Service Worker:他們如何運做以及應該注意的地方。最後,咱們還列出了您應該利用的Service Workers的一些獨特優點。promise
若是你想了解關於Service Workers的一切,你應該首先閱讀本系列第幾篇文章。瀏覽器
基本上,Service Worker是一種網絡工做者,更具體地說,它就像一個Shared Worker:緩存
Service Worker API使人興奮的主要緣由之一是它可讓你的網絡應用程序支持離線體驗,從而使開發人員可以徹底控制流程。安全
Service Worker生命週期與您的網頁是徹底分開。它由如下幾個階段組成:網絡
這是瀏覽器下載包含Service Worker的.js文件的時候。併發
您的Web應用程序想要安裝Service Worker,您必須先註冊它,您能夠在JavaScript代碼中進行註冊。當Service Worker被註冊時,它會提示瀏覽器在後臺啓動Service Worker安裝步驟。app
經過註冊Service Worker,你能夠告訴瀏覽器你的Service Worker的JavaScript文件在哪裏。咱們來看下面的代碼:oop
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事件(咱們將在後面討論)。若是咱們在/example/sw.js註冊Service Worker文件,那麼服務工做者將只能看到URL以/example/(即/example/page1/,/example/page2/)開頭的頁面的fetch事件。
在安裝階段,最好加載和緩存一些靜態資源。資源成功緩存後,Service Worker安裝完成。若是沒有成功(加載失敗) - Service Worker將重試。一旦安裝成功,您將知道靜態資源位於緩存中。
在安裝階段,最好加載和緩存一些靜態資源。資源成功緩存後,Service Worker安裝完成。若是沒有成功(加載失敗) - Service將重試。一旦安裝成功,您將知道靜態資產位於緩存中。
若是註冊須要在加載事件以後發生,這將回答您的問題。這不是必須的,但它是絕對推薦的。
爲何這樣?讓咱們考慮用戶第一次訪問您的網絡應用程序。目前尚未Service Worker,瀏覽器沒法事先知道是否會有安裝的Service Worker。若是安裝了Service Worker,則瀏覽器須要爲這個額外的線程花費額外的CPU和內存,不然瀏覽器將花費在渲染網頁上。
最重要的是,若是你只是在你的頁面上安裝一個Service Worker,你可能會有延遲加載和渲染的風險 - 而不是儘快讓你的用戶可使用這個頁面。
請注意,這僅在第一次訪問頁面時才顯得重要。後續頁面訪問不受Service Worker安裝的影響。一旦在第一次訪問頁面時激活Service Worker,它能夠處理加載/緩存事件,以便隨後訪問您的Web應用程序。這一切都是有道理的,由於它須要準備好處理有限的網絡鏈接。
安裝Service Worker以後,下一步是將其激活。這一步是管理之前緩存的好機會。
一旦激活,Service Worker將開始控制全部屬於其範圍的頁面。一個有趣的事實是:首次註冊Service Worker的頁面將不會被控制,直到該頁面再次被加載。一旦處於Service Worker控制之下,它將處於如下狀態之一:
如下是生命週期的外觀:
在頁面加速註冊過程以後,讓咱們看看Service Worker腳本中發生了什麼,它經過向Service Worker實例添加事件偵聽器來處理install事件。
這些是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 takes a promise to know how // long the installation takes, and whether it // succeeded or not. event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { console.log('Opened cache'); return cache.addAll(urlsToCache); }) ); });
若是全部文件成功被緩存,Service Worker會被安裝。若是任何文件下載失敗,install步驟會失敗。因此主要注意你須要緩存的文件。
處理install事件是可選的,你能夠避免處理它。在這種狀況下,上面三個步驟你都無需處理。
這部分是關鍵所在。這裏您將看到如何攔截請求並返回建立的緩存(並建立新緩存)。
安裝Service Worker而且用戶導航到另外一個頁面或刷新他所在的頁面後,Service Worker將收到fetch事件。下面是一個演示如何返回緩存資源或執行新請求而後緩存結果的示例:
self.addEventListener('fetch', function(event) { event.respondWith( // This method looks at the request and // finds any cached results from any of the // caches that the Service Worker has created. caches.match(event.request) .then(function(response) { // If a cache is hit, we can return thre response. if (response) { return response; } // Clone the request. A request is a stream and // can only be consumed once. Since we are consuming this // once by cache and once by the browser for fetch, we need // to clone the request. var fetchRequest = event.request.clone(); // A cache hasn't been hit so we need to perform a fetch, // which makes a network request and returns the data if // anything can be retrieved from the network. return fetch(fetchRequest).then( function(response) { // Check if we received a valid response if(!response || response.status !== 200 || response.type !== 'basic') { return response; } // Cloning the response since it's a stream as well. // 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) { // Add the request to the cache for future queries. cache.put(event.request, responseToCache); }); return response; } ); }) ); });
簡而言之,這就是發生的過程:
請求和響應必須被克隆,由於它們是流。流的主體只能被使用一次。並且因爲咱們想要使用它們,瀏覽器也要使用它們,因此必需克隆它們。
當用戶訪問您的Web應用程序時,瀏覽器會嘗試從新下載包含Service Worker代碼的.js文件。這發生在後臺。
若是與當前Service Worker的文件相比,如今下載的Service Worker文件中甚至存在單字節差別,則瀏覽器將假定有改變而且必須啓動新的Service Worker。
新的Service Worker將啓動而且install事件將被觸發。然而,在這一點上,舊的Service Worker仍在控制你的網絡應用程序的頁面,這意味着新的Service Worker將進入waiting狀態。
一旦您的Web應用程序當前打開的頁面關閉,舊的Service Worker將被瀏覽器終止,新安裝的Service Worker將徹底控制。這是當它的激活事件將被觸發。
爲何須要這些?爲了不兩個版本的Web應用程序同時運行在不一樣的選項卡上 - 這在網絡上實際上很是常見,而且可能會建立很是糟糕的錯誤(例如,在瀏覽器中存儲數據時存在不一樣模式的狀況)。
activate回調中最多見的步驟是緩存管理。你如今要這樣作,由於若是你在安裝步驟中刪除了全部舊的緩存,舊的Service Workers將忽然中止提供緩存中的文件。
下面是一個示例,您能夠從緩存中刪除某些未列入白名單的文件(在這種狀況下,其名稱中包含page-1或page-2):
self.addEventListener('activate', function(event) { var cacheWhitelist = ['page-1', 'page-2']; event.waitUntil( // Retrieving all the keys from the cache. caches.keys().then(function(cacheNames) { return Promise.all( // Looping through all the cached files. cacheNames.map(function(cacheName) { // If the file in the cache is not in the whitelist // it should be deleted. if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) ); });
在構建Web應用程序時,您能夠經過本地主機使用Service Worker,可是一旦將其部署到生產環境中,則須要準備好HTTPS(這是您擁有HTTPS的最後一個緣由)。
使用Service Worker,您能夠劫持鏈接並製做響應。 經過不使用HTTPs,您的Web應用程序變得容易受到攻擊。
爲了讓事情更安全,您須要在經過HTTPS提供的頁面上註冊Service Worker,以便您知道瀏覽器接收的Service Worker在經過網絡中未被修改。
服務人員的瀏覽器支持正在變得愈來愈好:
Service Worker提供的一些獨特功能是: