一篇文章讓你了解 service-worker
Service Worker 是什么?
service worker 是獨立于當前頁面的一段運行在瀏覽器后臺進程里的腳本。
service worker不需要用戶打開 web 頁面,也不需要其他交互,異步地運行在一個完全獨立的上下文環境,不會對主線程造成阻塞。基于service worker可以實現消息推送,靜默更新以及地理圍欄等服務。
service worker提供一種漸進增強的特性,使用特性檢測來漸漸增強,不會在老舊的不支持 service workers 的瀏覽器中產生影響。可以通過service workers解決讓應用程序能夠離線工作,讓存儲數據在離線時使用的問題。
注意事項:
1.service worker運行在它們自己的完全獨立異步的全局上下文中,也就是說它們有自己的容器。
2.service worker沒有直接操作DOM的權限,但是可以通過postMessage方法來與Web頁面通信,讓頁面操作DOM。
3.service worker是一個可編程的網絡代理,允許開發者控制頁面上處理的網絡請求。
4.瀏覽器可能隨時回收service worker,在不被使用的時候,它會自己終止,而當它再次被用到的時候,會被重新激活。
5.service worker的生命周期是由事件驅動的而不是通過Client。
Service Worker生命周期
service worker擁有一個完全獨立于Web頁面的生命周期
-
注冊service worker,在網頁上生效
-
安裝成功,激活 或者 安裝失敗(下次加載會嘗試重新安裝)
-
激活后,在sw的作用域下作用所有的頁面,首次控制sw不會生效,下次加載頁面才會生效。
-
sw作用頁面后,處理fetch(網絡請求)和message(頁面消息)事件 或者 被終止(節省內存)。
Service Worker支持使用
瀏覽器支持
polyfill
使用 ServiceWorker cache polyfill 讓舊版本瀏覽器支持 ServiceWorker cache API,
https
Server需要支持https
通過service worker可以劫持連接,偽造和過濾響應,為了避免這些問題,只能在HTTPS的網頁上注冊service workers,防止加載service worker的時候不被壞人篡改。
Github Pages是HTTPS的,可以通過Github做一些嘗試
調試工具
在調試的時候可以用于unregister、stop、start等操作
chrome訪問 chrome://inspect/#service-workers 或 chrome://serviceworker-internals 查看service-workers
firefox通過 about:debugging#workers 查看
離線存儲數據
對URL尋址資源,使用 Cache API 。對其他數據,使用IndexedDB。
離線閱讀
demo
使用 https 訪問本文,打開ChromeDevTools,選擇Application選項卡->Service Workers
可以看到Service Workers注冊
點擊下面離線保存按鈕
然后選擇Cache Storage,可以看到文字內容已經緩存到Cache Storage
然后選擇Service Workers 勾選 Offline,NetWork出現了:warning:?,然后試試離線訪問本文:sunglasses:
原理
注冊 service worker
創建一個 JavaScript 文件(比如:sw.js)作為 service worker
告訴瀏覽器注冊這個JavaScript文件為service worker,檢查service worker API是否可用,如果可用就注冊service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
}
sw.js文件被放在這個域的根目錄下,和網站同源。這個service work將會收到這個域下的所有fetch事件。
如果將service worker文件注冊為/example/sw.js,那么,service worker只能收到/example/路徑下的fetch事件(例如: /example/page1/, /example/page2/)。
// Service Workers
if ('serviceWorker' in navigator && navigator.onLine) {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
var currentPath = window.location.pathname;
var cacheButton = document.querySelector('.offline-btn');
var imageArray = document.querySelectorAll('img');
if(cacheButton) {
cacheButton.addEventListener('click', function(event) {
event.preventDefault();
// 緩存當前鏈接和使用的圖片
var pageResources = [currentPath];
for (i = 0; i < imageArray.length; i++) {
pageResources.push(imageArray[i].src);
}
caches.open('offline-' + currentPath).then(function(cache) {
var updateCache = cache.addAll(pageResources);
updateCache.then(function() {
console.log('Article is now available offline.');
cacheButton.innerHTML = "?";
});
updateCache.catch(function (error) {
console.log('Article could not be saved offline.');
cacheButton.innerHTML = "?";
});
});
});
}
}</code></pre>
緩存站點的資源
定義需要緩存的文件,然后在sw注冊安裝后回到cache Api將資源文件寫入緩存。如果所有的文件都被緩存成功了,那么service worker就安裝成功了。如果任何一個文件下載失敗,那么安裝步驟就會失敗。
var cacheName = 'v1';
var assetsToCache = [
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(cacheName).then(function(cache) {
return cache.addAll(assetsToCache);
}).then(function() {
return self.skipWaiting();
})
);
});</code></pre>
從緩存中加載
service worker成功注冊,并且用戶瀏覽了另一個頁面或者刷新了當前的頁面,service worker將開始接收到fetch事件。
攔截網絡請求并使用緩存,緩存命中,返回緩存資源,否則返回一個實時從網絡請求fetch的結果。
self.addEventListener('fetch', function(event) {
var requestUrl = new URL(event.request.url);
if (requestUrl.origin === location.origin) {
if (requestUrl.pathname === '/') {
event.respondWith(
caches.open(cacheName).then(function(cache) {
return fetch(event.request).then(function(networkResponse) {
cache.put(event.request, networkResponse.clone());
return networkResponse;
}).catch(function() {
return cache.match(event.request);
});
})
);
}
}
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});</code></pre>
緩存版本管理
版本修改的時候會觸發activate,將舊版本的緩存清理掉。
var OFFLINE_PREFIX = 'offline-';
var CACHE_NAME = 'main_v1.0.0';
self.addEventListener('activate', function(event) {
var mainCache = [CACHE_NAME];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if ( mainCache.indexOf(cacheName) === -1 && cacheName.indexOf(OFFLINE_PREFIX) === -1 ) {
// When it doesn't match any condition, delete it.
console.info('SW: deleting ' + cacheName);
return caches.delete(cacheName);
}
})
);
})
);
return self.clients.claim();
});
Service Worker 庫
-
-
-
offline-plugin ,webpack離線插件
參考
來自:http://kailian.github.io/2017/03/01/service-worker