serviceWorker 能夠實現資源、網絡請求的緩存和處理,是 PWA 實現離線可訪問、穩定訪問、靜態資源緩存的一項重要技術javascript
if ("serviceWorker" in navigator) { navigator.serviceWorker.register("./sw.js").then(function (registeration) { console.log(registeration); }); }
// sw.js 文件 var VERSION = "v1"; // install 階段:安裝sw,以後請求資源,並將資源緩存在cacheStorage中 self.addEventListener("install", function (event) { event.waitUntil( caches.open(VERSION).then(function (cache) { return cache.addAll([ "./start.html", "./static/jquery.min.js", "./static/mm1.jpg", ]); }) ); }); // 激活階段:能夠作些 除舊迎新的工做 // self.clients.matchAll() 能夠獲取瀏覽器全部的標籤 self.addEventListener("activate", function (event) { event .waitUntil( caches.keys().then(function (cacheNames) { return Promise.all( cacheNames.map(function (cacheName) { // 若是當前版本和緩存版本不一致 if (cacheName !== VERSION) { return caches.delete(cacheName); } }) ); }) ) .then(function (cache) { // 這裏能夠判斷若是cache裏原本沒有內容,表示第一次安裝,就不用通知用戶了 return self.clients.matchAll().then(function (clients) { if (clients && clients.length) { clients.forEach(function (client) { // 給每一個已經打開的標籤都 postMessage client.postMessage("sw.update"); }); } }); }); }); // 攔截fetch請求階段:捕獲請求並返回緩存數據 self.addEventListener("fetch", function (event) { event.respondWith( caches .match(event.request) .catch(function () { return fetch(event.request); }) .then(function (response) { caches.open(VERSION).then(function (cache) { cache.put(event.request, response); }); return response.clone(); }) .catch(function () { return caches.match("./static/mm1.jpg"); }) ); });
navigator.serviceWorker.addEventListener("message", (event) => { console.log(event.data); });
scope 屬性控制當前 sw 的做用範圍
if ("serviceWorker" in navigator) { navigator.serviceWorker .register("./sw.js", { scope: "/demo" }) .then(function (registration) { console.log(registration); }); } // {scope:'/'} 表示做用在全部頁面 // navigator.serviceWorker.register('/foo/sw.js',{scope:'/'}) 不會成功,由於sw 在foo路徑下,而想要做用的倒是全局;越界了
當一個項目,存在多個 sw 時,會出現 sw 彼此干擾的問題(好比一個資源在多個 sw 做用域中);能夠經過在註冊以前,先行 unregister 的方法來解決html
navigator.serviceWorker.getRegistration() 獲取頁面全部註冊的 serviceWorker
navigator.serviceWorker.getRegistration().then(function (regs) { for (var reg of regs) { reg.unregister(); } });
一、服務器端控制前端
location ~ \/sw\.js${ add_header Cache-Control no-store; add_header Pragma no-cache; }
二、前端使用版本控制java
// 這種方式不行,會觸發死循環 if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/sw.js?v=" + Date.now()); }
使用 sw-register.js 文件jquery
// sw-register.js if('serviceWorker' in navigator){ navigator.serviceWorker.register('/sw.js?v=201807041262') } // index.html <script> window.onload=function(){ var script=document.createElement('script'); var firstScript=this.document.getElementByTagName('script')[0]; script.type="text/javascript"; script.async=true; script.src="/sw-register.js?v="+Date.now(); firstScript.parentNode.insertBefore(script,firstScript) } </script>
Q:若是 SW 運行過程當中,出現了問題怎麼辦?
A: 須要 找個能快速上線的開關 JS 文件 https://yourhost.com/switch.jsgit
默認 SW_FALLBACK=false ;緊急狀況 SW_FALLBACK=true;github
<script> window.onload=function(){ var firstScript=document.getElementByTagName('script')[0]; var fbScript=document.createElement("script"); fbScript.type="text/javascript"; fbScript.async=true; fbScript.src="https://yourhost.com/switch.js?v="+Date.now(); firstScript.parentNode.insertBefore(fbScript,firstScript); fbScript.onload=function(){ if('serviceWorker' in navigator && window.SW_FALLBACK){ //getRegistration 的參數爲sw的scope值 navigator.serviceWorker.getRegistration('/').then(function(reg){ reg && reg.unregister(); }) } } } </script>
sw 應用場景不少,好比後端消息推送等等,由於時間緣由,沒有詳細瞭解這塊,能夠參考這篇
使用 Service Worker 發送 Push 推送chrome
總體文章,可能會有些問題,若有疑問,歡迎留言或者微信交流,共同窗習進步!後端
ServiceWorker MDN
Service Worker 最佳實踐
Service Worker 應用
service-worker
藉助 Service Worker 和 cacheStorage 緩存及離線開發