摘要:的本質是一個,它獨立于主線程,因此它不能直接訪問,也不能直接訪問對象,但是,可以訪問對象,也可以通過消息傳遞的方式與主線程進行通信。的最佳用法其實就是配合做離線緩存。
什么是Service Worker
Service Worker本質上充當Web應用程序與瀏覽器之間的代理服務器,也可以在網絡可用時作為瀏覽器和網絡間的代理。它們旨在(除其他之外)使得能夠創建有效的離線體驗,攔截網絡請求并基于網絡是否可用以及更新的資源是否駐留在服務器上來采取適當的動作。他們還允許訪問推送通知和后臺同步API。
Service Worker的本質是一個Web Worker,它獨立于JavaScript主線程,因此它不能直接訪問DOM,也不能直接訪問window對象,但是,Service Worker可以訪問navigator對象,也可以通過消息傳遞的方式(postMessage)與JavaScript主線程進行通信。
Service Worker是一個網絡代理,它可以控制Web頁面的所有網絡請求。
Service Worker具有自身的生命周期,使用好Service Worker的關鍵是靈活控制其生命周期。
Service Worker的作用用于瀏覽器緩存
實現離線Web APP
消息推送
Service Worker兼容性Service Worker是現代瀏覽器的一個高級特性,它依賴于fetch API、Cache Storage、Promise等,其中,Cache提供了Request / Response對象對的存儲機制,Cache Storage存儲多個Cache。
示例在了解Service Worker的原理之前,先來看一段Service Worker的示例:
self.importScripts("./serviceworker-cache-polyfill.js"); var urlsToCache = [ "/", "/index.js", "/style.css", "/favicon.ico", ]; var CACHE_NAME = "counterxing"; self.addEventListener("install", function(event) { self.skipWaiting(); event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { return cache.addAll(urlsToCache); }) ); }); self.addEventListener("fetch", function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; } return fetch(event.request); }) ); }); self.addEventListener("activate", function(event) { var cacheWhitelist = ["counterxing"]; event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) ); });
下面開始逐段逐段地分析,揭開Service Worker的神秘面紗:
polyfill首先看第一行:self.importScripts("./serviceworker-cache-polyfill.js");,這里引入了Cache API的一個polyfill,這個polyfill支持使得在較低版本的瀏覽器下也可以使用Cache Storage API。想要實現Service Worker的功能,一般都需要搭配Cache API代理網絡請求到緩存中。
在Service Worker線程中,使用importScripts引入polyfill腳本,目的是對低版本瀏覽器的兼容。
Cache Resources List And Cache Name之后,使用一個urlsToCache列表來聲明需要緩存的靜態資源,再使用一個變量CACHE_NAME來確定當前緩存的Cache Storage Name,這里可以理解成Cache Storage是一個DB,而CACHE_NAME則是DB名:
var urlsToCache = [ "/", "/index.js", "/style.css", "/favicon.ico", ]; var CACHE_NAME = "counterxing";Lifecycle
Service Worker獨立于瀏覽器JavaScript主線程,有它自己獨立的生命周期。
如果需要在網站上安裝Service Worker,則需要在JavaScript主線程中使用以下代碼引入Service Worker。
if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/sw.js").then(function(registration) { console.log("成功安裝", registration.scope); }).catch(function(err) { console.log(err); }); }
此處,一定要注意sw.js文件的路徑,在我的示例中,處于當前域根目錄下,這意味著,Service Worker和網站是同源的,可以為當前網站的所有請求做代理,如果Service Worker被注冊到/imaging/sw.js下,那只能代理/imaging下的網絡請求。
可以使用Chrome控制臺,查看當前頁面的Service Worker情況:
安裝完成后,Service Worker會經歷以下生命周期:
下載(download)
安裝(install)
激活(activate)
用戶首次訪問Service Worker控制的網站或頁面時,Service Worker會立刻被下載。之后至少每24小時它會被下載一次。它可能被更頻繁地下載,不過每24小時一定會被下載一次,以避免不良腳本長時間生效。
在下載完成后,開始安裝Service Worker,在安裝階段,通常需要緩存一些我們預先聲明的靜態資源,在我們的示例中,通過urlsToCache預先聲明。
在安裝完成后,會開始進行激活,瀏覽器會嘗試下載Service Worker腳本文件,下載成功后,會與前一次已緩存的Service Worker腳本文件做對比,如果與前一次的Service Worker腳本文件不同,證明Service Worker已經更新,會觸發activate事件。完成激活。
如圖所示,為Service Worker大致的生命周期:
install在安裝完成后,嘗試緩存一些靜態資源:
self.addEventListener("install", function(event) { self.skipWaiting(); event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { return cache.addAll(urlsToCache); }) ); });
首先,self.skipWaiting()執行,告知瀏覽器直接跳過等待階段,淘汰過期的sw.js的Service Worker腳本,直接開始嘗試激活新的Service Worker。
然后使用caches.open打開一個Cache,打開后,通過cache.addAll嘗試緩存我們預先聲明的靜態文件。
監聽fetch,代理網絡請求頁面的所有網絡請求,都會通過Service Worker的fetch事件觸發,Service Worker通過caches.match嘗試從Cache中查找緩存,緩存如果命中,則直接返回緩存中的response,否則,創建一個真實的網絡請求。
self.addEventListener("fetch", function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; } return fetch(event.request); }) ); });
如果我們需要在請求過程中,再向Cache Storage中添加新的緩存,可以通過cache.put方法添加,看以下例子:
self.addEventListener("fetch", function(event) { event.respondWith( caches.match(event.request) .then(function(response) { // 緩存命中 if (response) { return response; } // 注意,這里必須使用clone方法克隆這個請求 // 原因是response是一個Stream,為了讓瀏覽器跟緩存都使用這個response // 必須克隆這個response,一份到瀏覽器,一份到緩存中緩存。 // 只能被消費一次,想要再次消費,必須clone一次 var fetchRequest = event.request.clone(); 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; } ); }) ); });
在項目中,一定要注意控制緩存,接口請求一般是不推薦緩存的。所以在我自己的項目中,并沒有在這里做動態的緩存方案。activate
Service Worker總有需要更新的一天,隨著版本迭代,某一天,我們需要把新版本的功能發布上線,此時需要淘汰掉舊的緩存,舊的Service Worker和Cache Storage如何淘汰呢?
self.addEventListener("activate", function(event) { var cacheWhitelist = ["counterxing"]; event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) ); });
首先有一個白名單,白名單中的Cache是不被淘汰的。
之后通過caches.keys()拿到所有的Cache Storage,把不在白名單中的Cache淘汰。
淘汰使用caches.delete()方法。它接收cacheName作為參數,刪除該cacheName所有緩存。
sw-precache-webpack-pluginsw-precache-webpack-plugin是一個webpack plugin,可以通過配置的方式在webpack打包時生成我們想要的sw.js的Service Worker腳本。
一個最簡單的配置如下:
var path = require("path"); var SWPrecacheWebpackPlugin = require("sw-precache-webpack-plugin"); const PUBLIC_PATH = "https://www.my-project-name.com/"; // webpack needs the trailing slash for output.publicPath module.exports = { entry: { main: path.resolve(__dirname, "src/index"), }, output: { path: path.resolve(__dirname, "src/bundles/"), filename: "[name]-[hash].js", publicPath: PUBLIC_PATH, }, plugins: [ new SWPrecacheWebpackPlugin( { cacheId: "my-project-name", dontCacheBustUrlsMatching: /.w{8}./, filename: "service-worker.js", minify: true, navigateFallback: PUBLIC_PATH + "index.html", staticFileGlobsIgnorePatterns: [/.map$/, /asset-manifest.json$/], } ), ], }
在執行webpack打包后,會生成一個名為service-worker.js文件,用于緩存webpack打包后的靜態文件。
一個最簡單的示例。
Service Worker Cache VS Http Cache對比起Http Header緩存,Service Worker配合Cache Storage也有自己的優勢:
緩存與更新并存:每次更新版本,借助Service Worker可以立馬使用緩存返回,但與此同時可以發起請求,校驗是否有新版本更新。
無侵入式:hash值實在是太難看了。
不易被沖掉:Http緩存容易被沖掉,也容易過期,而Cache Storage則不容易被沖掉。也沒有過期時間的說法。
離線:借助Service Worker可以實現離線訪問應用。
但是缺點是,由于Service Worker依賴于fetch API、依賴于Promise、Cache Storage等,兼容性不太好。
后話本文只是簡單總結了Service Worker的基本使用和使用Service Worker做客戶端緩存的簡單方式,然而,Service Worker的作用遠不止于此,例如:借助Service Worker做離線應用、用于做網絡應用的推送(可參考push-notifications)等。
甚至可以借助Service Worker,對接口進行緩存,在我所在的項目中,其實并不會做的這么復雜。不過做接口緩存的好處是支持離線訪問,對離線狀態下也能正常訪問我們的Web應用。
Cache Storage和Service Worker總是分不開的。Service Worker的最佳用法其實就是配合Cache Storage做離線緩存。借助于Service Worker,可以輕松實現對網絡請求的控制,對于不同的網絡請求,采取不同的策略。例如對于Cache的策略,其實也是存在多種情況。例如可以優先使用網絡請求,在網絡請求失敗時再使用緩存、亦可以同時使用緩存和網絡請求,一方面檢查請求,一方面有檢查緩存,然后看兩個誰快,就用誰。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97897.html
摘要:學習與實踐系列文章已整理至學習手冊,文字內容已同步至。本文是學習與實踐系列的第三篇文章。引言其中一個令人著迷的能力就是離線可用。但是,如果你注意到文章開頭的圖片就會發現,離線時我們不僅可以訪問,還可以使用搜索功能。 《PWA學習與實踐》系列文章已整理至gitbook - PWA學習手冊,文字內容已同步至learning-pwa-ebook。轉載請注明作者與出處。 本文是《PWA學習與實...
摘要:簡稱,是提升的體驗的一種新方法,能給用戶原生應用的體驗。當網站以這種方式啟動時它具有唯一的圖標和名稱,以便用戶將其與其他網站區分開來。表示啟動時的方向,橫屏豎屏等,具體參數值可參考文檔。下一篇文章中,主要講述在實踐中的重要能力。 這周,Chrome 70正式版本發布,Progressive Web Apps(PWA)已經正式支持到Windows 10平臺,然而,早在前幾個版本之前,Ch...
摘要:本文是學習與實踐系列的第五篇文章。實際上,消息推送與提醒是兩個功能和。在這一篇里,我們先來學習如何使用進行消息推送。而當服務端要推送消息時,會使用私鑰對發送的數據進行數字簽名,并根據數字簽名生成一個叫請求頭。 《PWA學習與實踐》系列文章已整理至gitbook - PWA學習手冊,文字內容已同步至learning-pwa-ebook。轉載請注明作者與出處。 本文是《PWA學習與實踐》系...
摘要:基本上是使用現代技術構建的網站但是體驗上卻像一個移動,在年,谷歌工程師和創造了。此后谷歌就一直致力于讓能給用戶像原生一般的體驗。檢查谷歌瀏覽器的和現在重載你的并且打開,到選項去查看面板,確保這個選項是勾選的。 Web開發多年來有了顯著的發展。它允許開發人員部署網站或Web應用程序并在數分鐘內為全球數百萬人服務。只需一個瀏覽器,用戶可以輸入URL就可以訪問Web應用程序了。隨著 Prog...
摘要:基本上是使用現代技術構建的網站但是體驗上卻像一個移動,在年,谷歌工程師和創造了。此后谷歌就一直致力于讓能給用戶像原生一般的體驗。檢查谷歌瀏覽器的和現在重載你的并且打開,到選項去查看面板,確保這個選項是勾選的。 Web開發多年來有了顯著的發展。它允許開發人員部署網站或Web應用程序并在數分鐘內為全球數百萬人服務。只需一個瀏覽器,用戶可以輸入URL就可以訪問Web應用程序了。隨著 Prog...
閱讀 746·2023-04-26 01:30
閱讀 3301·2021-11-24 10:32
閱讀 2179·2021-11-22 14:56
閱讀 1979·2021-11-18 10:07
閱讀 553·2019-08-29 17:14
閱讀 624·2019-08-26 12:21
閱讀 3103·2019-08-26 10:55
閱讀 2940·2019-08-23 18:09