\n

撰寫sw.js腳本檔

\n
 cacheUrl = [
'./index.html',
'./script.js',
'./car.svg'
];
const cacheName = 'precache' + (self.registration ? self.registration.scope: '');
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(cacheName)
.then((cache) => {
console.log('open cache');
return cache.addAll(cacheUrl);
});
);
});
//clean cached files
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then(function(cacheNames) {
var promiseArr = cacheNames.map(function(item) {
if (item !== cacheName) {
return caches.delete(item);
}
})
return Promise.all(promiseArr);
})
)
});
self.addEventListener('fetch', (event) => {
//cache first
event.respondWith(caches.match(event.request).then(res => {
if (res) {
console.log('match');
return res;
}
return fetch(event.request);
}));
});
\n的策略\n only\n
這種方式下任何請求都會從Cache storage取得
\n
.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request));
});
\n only\n
這種方式下任何請求都不會跟Cache storage打交道,直接向後端發送
\n
.addEventListener('fetch', function(event) {
event.respondWith(fetch(event.request));
// or simply don't call event.respondWith, which
// will result in default browser behaviour
});
\n first\n
頁面發送request時會先從Cache storage中存取若發現該請求尚未被緩存到則會改為Network請求
\n
.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});
\n first\n
頁面發送request時會先由Network向後端請求,若請求失敗則改由Cache storage請求
\n
.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).catch(function() {
return caches.match(event.request);
})
);
});
\n & network race\n
頁面發送request時同時向Cache及Network請求,哪一個請求先回來則使用該response
\n
// Promise.race is no good to us because it rejects if// a promise rejects before fulfilling. Let's make a proper// race function:function promiseAny(promises) {
return new Promise((resolve, reject) => {
// make sure promises are all promises
promises = promises.map(p => Promise.resolve(p));
// resolve this promise as soon as one resolves
promises.forEach(p => p.then(resolve));
// reject if all promises reject
promises.reduce((a, b) => a.catch(() => b))
.catch(() => reject(Error(All failed)));
});
}; self.addEventListener('fetch', function(event) {
event.respondWith(
promiseAny([
caches.match(event.request),
fetch(event.request)
])
);
});
\n then network\n
頁面發送request時先由Cache storage取得顯示給user,然後再由Network取得後更新資料。
\n in Page\n
 networkDataReceived = false;    startSpinner();    // fetch fresh data
var networkUpdate = fetch('/data.json').then(function(response) {
return response.json();
}).then(function(data) {
networkDataReceived = true;
updatePage();
}); // fetch cached data
caches.match('/data.json').then(function(response) {
if (!response) throw Error(No data);
return response.json();
}).then(function(data) {
// don't overwrite newer network data
if (!networkDataReceived) {
updatePage(data);
}
}).catch(function() {
// we didn't get cached data, the network is our last hope:
return networkUpdate;
}).catch(showErrorMessage).then(stopSpinner);
\n in the ServiceWorker: 當網路請求資源成功時,更新緩存內容\n
.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('mysite-dynamic').then(function(cache) {
return fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
});
})
);
});
\n

相關概念

\n\n

實際操作

\n worker相關的library\n-precache主要根據配置來產生service worker腳本檔的工具,而sw-toolbox則是sw-precache的加強工具,可以針對動態請求進行細部控制\n\n

各家瀏覽器支援狀況

","keywords":["web","資源","Google","伺服器","註冊","環境"],"author":{"@type":"Person","name":"阿Han","url":"https://vocus.cc/user/@weihanchen"},"image":{"@type":"ImageObject","url":"https://miro.medium.com/v2/resize:fit:560/0*nEDWR5JnAWigmYJe.png"},"creator":{"@type":"Person","name":"阿Han"},"publisher":{"@type":"Organization","name":"方格子|放送你的知識與想像","logo":{"@type":"ImageObject","url":"https://images.vocus.cc/static/og_img/vocus_kv.jpeg"}}}

【Web微知識系列】 Service Workers

閱讀時間約 15 分鐘

Service Workers

Service worker與Web workers相同,也都是一段運行在瀏覽器後台的腳本,提供一些不需要與頁面直接交互的功能(操作dom),主要處理網路相關的問題,可以攔截網路請求進行相對應的優化動作,我們把它想像成與伺服器之間的代理服務器可能會比較容易理解,當網路環境不佳時便回應快取資源,待網路順暢後同步最新資料,因此能提高更好的離線體驗,我們可能會想說為什麼有了Web workers、AppCache這類的API還需要Service worker呢?因為這些既有的功能主要都由我們自己去handle一些細緻的操作,過程非常繁瑣,因此發展出Service worker,背後幫我們解決掉許多事情(error handler、http request listener…)

功能

  • 資源快取
  • 離線應用
  • 多頁面傳遞(Post Message)
  • 推播通知
  • 後台自動更新

生命週期

  • 註冊Service worker
  • 註冊之後瀏覽器會在背景啟動Service Worker的安裝
  • 安裝過程中會將設定的靜態資源進行緩存,待所有靜態資源緩存成功後進入Activated狀態
  • 如果過程中任何一個資源不能成功緩存則代表安裝失敗,進入error,待重新安裝
  • 進入Activated狀態後進行監聽,當request或post message發生時則觸發相對應動作
  • Terminated狀態由瀏覽器決定是否銷毀,如果長期不使用或者記憶體不足時,則可能銷毀這個worker

Worker中常使用的事件

簡單實作Service Worker

註冊Service Worker

<html>
<body>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js')
.then(reg => console.log(reg))
.catch(err => console.log(err));
}
</script>
</body></html>

撰寫sw.js腳本檔

const cacheUrl = [
'./index.html',
'./script.js',
'./car.svg'
];
const cacheName = 'precache' + (self.registration ? self.registration.scope: '');
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(cacheName)
.then((cache) => {
console.log('open cache');
return cache.addAll(cacheUrl);
});
);
});
//clean cached files
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then(function(cacheNames) {
var promiseArr = cacheNames.map(function(item) {
if (item !== cacheName) {
return caches.delete(item);
}
})
return Promise.all(promiseArr);
})
)
});
self.addEventListener('fetch', (event) => {
//cache first
event.respondWith(caches.match(event.request).then(res => {
if (res) {
console.log('match');
return res;
}
return fetch(event.request);
}));
});

cache的策略

Cache only

這種方式下任何請求都會從Cache storage取得
self.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request));
});

Network only

這種方式下任何請求都不會跟Cache storage打交道,直接向後端發送
self.addEventListener('fetch', function(event) {
event.respondWith(fetch(event.request));
// or simply don't call event.respondWith, which
// will result in default browser behaviour
});

Cache first

頁面發送request時會先從Cache storage中存取若發現該請求尚未被緩存到則會改為Network請求
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});

Network first

頁面發送request時會先由Network向後端請求,若請求失敗則改由Cache storage請求
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).catch(function() {
return caches.match(event.request);
})
);
});

Cache & network race

頁面發送request時同時向Cache及Network請求,哪一個請求先回來則使用該response
// Promise.race is no good to us because it rejects if// a promise rejects before fulfilling. Let's make a proper// race function:function promiseAny(promises) {
return new Promise((resolve, reject) => {
// make sure promises are all promises
promises = promises.map(p => Promise.resolve(p));
// resolve this promise as soon as one resolves
promises.forEach(p => p.then(resolve));
// reject if all promises reject
promises.reduce((a, b) => a.catch(() => b))
.catch(() => reject(Error("All failed")));
});
}; self.addEventListener('fetch', function(event) {
event.respondWith(
promiseAny([
caches.match(event.request),
fetch(event.request)
])
);
});

Cache then network

頁面發送request時先由Cache storage取得顯示給user,然後再由Network取得後更新資料。
Code in Page
var networkDataReceived = false;    startSpinner();    // fetch fresh data
var networkUpdate = fetch('/data.json').then(function(response) {
return response.json();
}).then(function(data) {
networkDataReceived = true;
updatePage();
}); // fetch cached data
caches.match('/data.json').then(function(response) {
if (!response) throw Error("No data");
return response.json();
}).then(function(data) {
// don't overwrite newer network data
if (!networkDataReceived) {
updatePage(data);
}
}).catch(function() {
// we didn't get cached data, the network is our last hope:
return networkUpdate;
}).catch(showErrorMessage).then(stopSpinner);
Code in the ServiceWorker: 當網路請求資源成功時,更新緩存內容
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('mysite-dynamic').then(function(cache) {
return fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
});
})
);
});

相關概念

  • Service worker與Web worker一樣不能直接對dom結構進行操作,僅透過postMessage相互溝通
  • 基於安全考量Service worker只能運行在https的環境之上,畢竟修改網路請求的能力是相對危險的
  • 使用Service worker來進行cache優於一般worker的原因是Service worker能夠細緻的控制,例如發生error時直接於worker內部進行相對應處理,而一般worker僅能於產生worker的那個頁面進行,且API相對較少

實際操作

Service worker相關的library

sw-precache主要根據配置來產生service worker腳本檔的工具,而sw-toolbox則是sw-precache的加強工具,可以針對動態請求進行細部控制

各家瀏覽器支援狀況

為什麼會看到廣告
96會員
237內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
留言0
查看全部
發表第一個留言支持創作者!
你可能也想看
創作者要怎麼好好休息 + 避免工作過量?《黑貓創作報#4》午安,最近累不累? 這篇不是虛假的關心。而是《黑貓創作報》發行以來可能最重要的一篇。 是的,我們這篇講怎麼補充能量,也就是怎麼休息。
Thumbnail
avatar
黑貓老師
2024-06-29
防曬產品係數測試報告彙整(2024年)從2014年起,自己對於市售防曬產品的效能產生了濃厚的興趣。因為當時候發現不少產品的防曬係數其實標示是有問題的,像是原本應該是人體測試的SPF與PA數值,實際上沒有做,只用機器測試的數據來充當,但這兩者卻有很大的差異。像是防曬係數其實有強度、廣度與平均度三個面向需要一起判斷,但多數廠商並沒有完整標示
Thumbnail
avatar
邱品齊皮膚科醫師
2023-04-27
【Web Frontend】限制 iframe 的尺寸上限又能夠有 RWD 作為一個非常不專業的前端初學者,有陣子常常卡在公司官網,要插入 Youtube 影片無法RWD(響應式)的問題。 跟不熟悉 網頁技術的朋友們介紹,RWD 就是指網頁的排版能跟著螢幕的大小縮放、變化編排,在這個人手一機的時代,特別重要。
Thumbnail
avatar
切格拉底
2024-06-12
Web Workers 調用 API,減輕主執行緒負擔透過 Web Workers,您可以將這些耗時的操作放在另一個執行緒中處理,減輕主執行緒的負擔,提高網站的效能和響應速度。這篇文章提供了詳細的解釋和示例,幫助您快速上手使用 Web Workers。不要錯過這個可以改善網站效能的實用技巧!
Thumbnail
avatar
Let's Write
2023-11-05
Web 3.0 的未來前景:一窺數位世界的明日—社群媒體篇(下)「日常型」社群平台,如Facebook,聯繫朋友和家人,加入各種社團!「生活型」社群平台,如Instagram和小紅書,分享生活的美好時刻!「新聞型」社群平台,如Twitter和Threads,緊跟即時快訊,了解世界大事!「娛樂型」社群平台,如Youtube和TikTok,這裡是休閒娛樂的樂園!
Thumbnail
avatar
刺蝟之光
2023-09-14
Web 3.0 的未來前景:一窺數位世界的明日—社群媒體篇(上)本文講述了社群媒體帶來的個人效益和平台效益。其中提到了建立個人品牌的重要性,以及專業交流、學習、創作內容和自媒體的優勢。此外,社交聯繫和友誼也是社群媒體的一個重要面向。文章還探討了社群媒體的規模經濟和經濟效益,以及市場競爭和廣告效益。最後,提到了社群媒體的創新效益,這是一個不斷演進和發展的領域。
Thumbnail
avatar
刺蝟之光
2023-09-14
Web 3.0 的未來前景:一窺數位世界的明日—正文篇本文探討社群媒體的過去、現在,以及Web 3.0的未來!😲回顧社群媒體的發展史,從FB的統治到IG的崛起,再到Twitter被改名成X。現代社交媒體和串流平台為我們提供了更多的選擇和自由。Web 3.0利用機器學習、人工智慧和區塊鏈技術,使網路更聰明、更分散、更關心隱私。你會接受新時代的知識嗎?
Thumbnail
avatar
刺蝟之光
2023-09-12
WEB 3.0能實現創作者終極自由的烏托邦嗎?P.S. 這是一篇發表於去年在另一個帳號的舊文,因為vocus無法變更帳號名,所以我把這篇文章搬過來新帳號重新發表一次。 創作者追求的終極自由作為一名創作者,我夢想達到終極自由,這個終極自由有三個層面: 經濟上的自由/創作上的自由/時空上的自由 經濟上的自由就是終於可以不用白天兼一份工來支持自己
Thumbnail
avatar
KT 🦄 凱婷
2023-04-09
【Web 3】| 開啟星爺的 Web 3.0 元宇宙|周星馳這次也要打十個嗎?web 3.0的未來是接近了!(目前 Web 2.5) 1.目前幣圈、NFT的門檻相當高。 (各國金融與法律制度是關鍵) 2.Web3.0的人才是什麼類型的? (這裡可能要做點research,但厲害的人到哪裡都可以適應) 3. 星爺左邊的圖表才是關鍵,大概是未來的 Road map 。 (我大膽猜
Thumbnail
avatar
Dodson
2022-10-21
Web 3.0的去中心化是未來嗎? 關於中心化與去中心化的二三事近期隨著區塊鏈應用越來越多,有些區塊鏈的支持者開始喊出「Web 3.0」的口號。他們認為去中心化的區塊鏈將成為新的 Web 3.0,取代因為網路巨頭而過度集中的 Web 2.0,成為眾人的網路。真的如此嗎?我認為,我們應該可以從歷史的角度回過頭來重新思考「去中心化」與「中心化」的關係。
Thumbnail
avatar
林雨蒼
2022-03-23
Web 3.0 入場券,我的第一個NFT什麼是Web3.0? Web3.0 ≠ NFT ≠ 虛擬貨幣 ≠ 去中心化 簡單來說,Web3.0 可以說是網際網路的下一個世代技術的「集合總稱」。需要有很多的軟硬體設備同時都互相配合支援才會走到的「世代技術」。最底層硬體技術,我個人覺得是「上網速度的提昇」以及終端機「運算元的速度提昇」。
Thumbnail
avatar
Okuma 多元體
2022-02-28