日常瀏覽器窗口中跑的頁面運行的是主JavaScript線程,DOM和window全局變量都是能夠訪問的。html
Service Worker是走的另外的線程,能夠理解爲在瀏覽器背後默默運行的一個線程,或者說是獨立於當前頁面的一段運行在瀏覽器後臺進程裏的腳本。webpack
它脫離瀏覽器窗體,異步地運行在一個徹底獨立的上下文環境,不會對主線程形成阻塞。在service worker中,window以及DOM都是不能訪問的,但可使用self訪問全局上下文。web
1.離線緩存(重點) 2.消息推送(重點) 3.後臺數據同步 4.響應來自其它源的資源請求, 5.集中接收計算成本高的數據更新,好比地理位置和陀螺儀信息,這樣多個頁面就能夠利6.用同一組數據 7.在客戶端進行CoffeeScript,LESS,CJS/AMD等模塊編譯和依賴管理(用於開發目的) 8.後臺服務鉤子 9.自定義模板用於特定URL模式 10.性能加強,好比預取用戶可能須要的資源,好比相冊中的後面數張圖片chrome
一、https: Service Worker必須是https協議的,但本地環境下http://localhost或者http://127.0.0.1也能夠的。 二、瀏覽器兼容性: 瀏覽器
一、chrome://serviceworker-internals 緩存
二、網頁中ApplicationService Worker生命週期的反應: installing → installed → activating → activated 'install'用來緩存文件,'activate'用來緩存更新 bash
一、html中異步
if ('serviceWorker' in navigator) {
// 開始註冊service workers
navigator.serviceWorker.register('./sw-demo-cache.js', {
scope: './'
}).then(function (registration) {
console.log('註冊成功');
var serviceWorker;
if (registration.installing) {
serviceWorker = registration.installing;
console.log('安裝installing');
} else if (registration.waiting) {
serviceWorker = registration.waiting;
console.log('等待waiting');
} else if (registration.active) {
serviceWorker = registration.active;
console.log('激活active');
}
console.log('=>serviceWorker:', serviceWorker);
if (serviceWorker) {
console.log(serviceWorker.state);
serviceWorker.addEventListener('statechange', function (e) {
console.log(' 狀態變化爲', e.target.state);
});
// 建立信道
var channel = new MessageChannel();
// port1留給本身
channel.port1.onmessage = e => {
console.log('main thread receive message...');
console.log(e);
}
console.log('給對方', window.RES_MAP);
// port2給對方
serviceWorker.postMessage(window.RES_MAP, [channel.port1]);
serviceWorker.addEventListener('statechange', function (e) {
// logState(e.target.state);
});
}
}).catch(function (error) {
console.log('註冊沒有成功');
});
} else {
console.log('不支持');
}
複製代碼
二、引進sw-demo-cache.jswordpress
// sw
self.addEventListener('message', ev => {
console.log('sw receive message..');
console.log(ev);
fileMap = ev.data.RES_MAP;
var arr1 = [].slice.call(fileMap); // ['a', 'b', 'c']
// 取main thread傳來的port2
ev.ports[0].postMessage('Hi, hello too');
});
// var fs = require('fs');
// console.log(fs);
// 緩存
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(VERSION).then(function(cache) {
return cache.addAll([
'./index.html',
]);
})
);
});
// 緩存更新
self.addEventListener('activate', function(event) {
console.log('two now ready to handle fetches!');
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
console.log('cacheName:', cacheName);
// 若是當前版本和緩存版本不一致
if (cacheName !== VERSION) {
return caches.delete(cacheName);
}
})
);
})
);
});
// 捕獲請求並返回緩存數據
self.addEventListener('fetch', function (event) {
try{
event.respondWith(
caches.match(event.request).then(function(res){
if(res){
return res;
}
requestBackend(event);
})
)
} catch {
console.log(event);
}
});
function requestBackend(event){
var url = event.request.clone();
return fetch(url).then(function(res){
//if not a valid response send the error
if(!res || res.status !== 200 || res.type !== 'basic'){
return res;
}
var response = res.clone();
console.log('VERSION:', VERSION);
caches.open(VERSION).then(function(cache){
cache.put(event.request, response);
});
return res;
})
}
複製代碼
三、webapck中獲取文件目錄 引入第三個模塊glob,遞歸獲取打包後的文件目錄post
exports.resMap = function () {
var entryFiles = glob.sync(PAGE_PATH + '/*/*.js')
var map = {}
entryFiles.forEach((filePath) => {
var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'))
map[filename] = filePath;
})
var entryFiles2 = glob.sync(PAGE_PATH2 + '/*')
var map2 = {}
findPath(entryFiles2, map2);
console.log('map2', map2);
return map2;
};
function findPath(entryFiles2, map2) {
entryFiles2.forEach(filePath => {
var filename = filePath.substring(filePath.lastIndexOf('/') + 1, filePath.lastIndexOf('.'));
if (filePath.indexOf('.') <= 0) {
let pathRes = path.resolve(__dirname, filePath);
let files = glob.sync(pathRes + '/*');
findPath(files, map2);
map2[filename] = filePath;
}
map2[filename] = filePath;
});
}
複製代碼
四、導出目錄 經過webpack的DefinePlugin插件,導出上步獲取的目錄 五、web和service worker的通訊 經過postMessage實現web和service worker間的通訊
// 建立信道
var channel = new MessageChannel();
// port1留給本身
channel.port1.onmessage = e => {
console.log('main thread receive message...');
console.log(e);
}
console.log('給對方', window.RES_MAP);
// port2給對方
serviceWorker.postMessage(window.RES_MAP, [channel.port1]);
serviceWorker.addEventListener('statechange', function (e) {
// logState(e.target.state);
});
複製代碼
// sw
self.addEventListener('message', ev => {
console.log('sw receive message..');
console.log(ev);
fileMap = ev.data.RES_MAP;
var arr1 = [].slice.call(fileMap); // ['a', 'b', 'c']
// 取main thread傳來的port2
ev.ports[0].postMessage('Hi, hello too');
});
複製代碼
一、service worker讓離線緩存成爲可能,offline狀況下也能夠訪問頁面。然而銷燬比較困難,更新會有問題。目前只能chrome://serviceworker-internals手動銷燬,還待研究。