[FE] Service Worker、Web Worker 和 Shared Worker

更新於 2024/02/29閱讀時間約 21 分鐘
Service Worker、Web Worker 和 Shared Worker

Service Worker

可以當作 Client Side 的攔截器,獲取網站內容的請求判斷(像是 axios 的 interceptor 或是 nginx 的 proxy),依據條件進行動作,可以使用 Cache Storage 和 IndexDB,基於事件驅動,並有自己的生命週期。註冊 Service Worker

'serviceWorker' in navigator &&
window.addEventListener('load', () => {
navigator.serviceWorker.register(new URL('./sw.js', import.meta.url));
});

Life Cycle

  • install 每個 Service Worker 只會執行一次,直到 Service Worker 更新
  • activate Service Worker 準備好控制 Client Site
  • activated完成 activate 後觸發的 event

透過 Life Cycle 進行 precaching 的範例

self.addEventListener('install', (event) => {
const cacheKey = '...';

event.waitUntil(caches.open(cacheKey).then((cache) => {

return cache.addAll([
...
]);
}));
});

在 install 時,先進行 precaching 並透過 event.waitUntil  處理非同步的 function。

self.addEventListener('activate', (event) => {
const cacheAllowList = [...];

event.waitUntil(caches.keys().then((keys) => {
return Promise.all(keys.map((key) => {
if (!cacheAllowList.includes(key)) {
return caches.delete(key);
}
}));
}));
});

在 activate 時刪除(修改) cache。

Update event

Service Worker 會在以下情況觸發 update 事件。

  • 使用者瀏覽的 url 在 Service Worker scope 內
  • 更改 scope 或是 Service Worker 的路徑 (不推薦)
  • 在過去 24H 內有觸發過 sync  或是 push  事件
  • 手動觸發
    • navigator.serviceWorker.ready.then((registration) => {
      registration.update();
      });

觸發更新後,Service Worker 將會下載新版本的 Service Worker 但並不會立刻激活,直到沒有打開的頁面是由舊的 Service Worker 控制時才會被激活。

Cache

Service Worker 可以搭配 JS 的 Cache Interface 建立 Cache,與 HTTP 的 Cache Header 並不相同,JS 的 Cache Interface 在較高的層級,完全獨立於 HTTP Cache。例如建立圖片的 Cache

self.addEventListener("fetch", (event) => {
if (event.request.destination === "image") {
event.responseWith(
cache.open(cacheName).then((cache) => {
return cache.match(event.request).then((cacheResponse) => {
if (cacheResponse) {
return cacheResponse;
}
return fetch(event.request.url).then((fetchResponse) => {
cache.put(event.request, fetchResponse.clone());
return fetchResponse;
});
});
})
);
}
});


Service Worker 搭配 Cache Interface 可以實現不同的 Cache Strategies。


Cache Only

raw-image

所有的 Resource 只透過 Cache 獲得,一開始進行資源的 Precaching ,直到 Service Worker 更新後才會進行更新。

// asset request url
const precachedAssets = [
'/pic1.jpg',
'/pic2.jpg',
'/pic3.jpg'
];

self.addEventListener('install', (event) => {
event.waitUntil(caches.open(cacheName).then(cache => {
return cache.addAll(precachedAssets)
}));
});

self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
const isPrecachedRequest = precachedAssets.includes(url.pathname);

if (isPrecachedRequest) {
event.responseWith(cache.open(cacheName).then(cache => {
return cache.match(event.request.url);
}));
} else {
// go to the network
return;
}
});

Network Only

raw-image

與 Cache Only 完全相反,沒有 Cache 只向 Network 獲取 Resource。

Cache First, falling back to network

raw-image

如果 Cache 沒有我們要的資源則從 Network 獲取,有的話優先從 Cache 獲得。

self.addEventListener("fetch", (event) => {
event.reponseWith(
caches.open(cacheName).then((cacheResponse) => {
if (cacheResponse) {
return cacheResponse;
}
return fetch(event.request).then((fetchResponse) => {
cache.put(event.request, fetchResponse.clone());
return fetchResponse;
});
})
);
});

適合用於 CSS, JavaScript, Image, Font 會經過 hash 的靜態資源。

Network First, falling back to cache

raw-image

優先向 Network 獲取資源,並將獲得的 Response 儲存至 Cache,如果是在 Offline 的情況下,則透過 Cache 獲得資料。

self.addEventListener("fetch", (event) => {
event.responseWith(
caches.open(cacheName).then((cache) => {
return fetch(event.request.url)
.then((fetchResponse) => {
cache.put(event.request, fetchResponse.clone());
return fetchResponse;
})
.cache(() => {
return cache.match(event.request.url);
});
})
);
});

適合希望獲取最新的資料,但若在 Offline 的時候可以獲取最新的 Cache,例如 HTML 或是 API。

State-while-revalidate

優先考量獲取資源的速度,並在後台進行更新。

self.addEventListener("fetch", (event) => {
event.respondWith(
caches.open(cacheName).then((cache) => {
return cache.match(event.request).then((cachedResponse) => {
const fetchedResponse = fetch(event.request).then((networkResponse) => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchedResponse;
});
})
);
});

適合用於需要保持資源狀態在最新,但並不是最重要的資料。

其他的用法

Web Push API

由 Client Side 向第三方 Server(Mozila, Google, Apple 等) 進行 Web Push 的註冊,Client Side 會帶著 VAPID Public Key 傳送給 Server,之後將獲取到的訂閱訊息傳送給 Bacdend。

async () => {
const notificationPermission = await Notification.requestPermission();
if (notificationPermission === "granted") {
const registration = await navigator.serviceWorker.ready;
const notificationSub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey:
"...", // VAPID public key
});
await fetch("BE Server", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(notificationSub),
});
}
})();

Backend 透過獲取到的訂閱資訊和 VAPID Private Key 發送 Request。

const vapidKeys = {
privateKey: "VAPID Private Key",
publicKey: "VAPID Public Key",
};
webPush.setVapidDetails(
"mailto:test@localhost.com",
vapidKeys.publicKey,
vapidKeys.privateKey
);
webPush.sendNotification(
req.body,
JSON.stringify({ title: "...", data: "..." })
);


第三方的 Web Push Server 會發送 Notification 給 Service Worker,由 Service Worker 觸發顯示 Notification。

self.addEventListener("push", (event) => {
const data = event.data.json();
const showNotification = async () => {
await self.registration.showNotification(data.title, { body: data.data });
console.log("showNotification");
};
event.waitUntil(showNotification());
});


Background Sync

當使用者網路不穩或是在 Offline 的環境時,將資料進行緩存,等待到有網路的時候在發送請求。

<form id="form">
<input name="name" /> <input name="message" />{" "}
<button type="submit">Send</button>
</form>;

透過 Cache Interface 進行儲存,並註冊 sync  事件。

document.getElementById("form").addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = { name: formData.get("name"), message: formData.get("message") };
const formCache = await caches.open("form");
await formCache.put("/data", new Response(JSON.stringify(data)));
if ("SyncManager" in window) {
const registration = await navigator.serviceWorker.ready;
registration.sync.register("form-sync");
}
});

在 Service Worker 中進行監聽並發送 Request。

self.addEventListener("sync", (event) => {
switch (event.tag) {
case "form-sync": {
async function submitFormData() {
const formCache = await caches.open("form");
const formDataRes = await formCache.match("/data");
const body = await formDataRes.json();
const resp = await fetch("http://localhost:3000/form-sync", {
method: "POST",
body: JSON.stringify(body),
headers: { "Content-Type": "application/json" },
});
const data = await resp.json();
console.log(data);
}
event.waitUntil(submitFormData());
break;
}
}
});

Period Background Sync

Interval 的 Background Sync,需要權限(User 有安裝 PWA)才可以進行操作,並依據 Site Engagement Score 來決定 sync 的頻率。

Web Worker

從瀏覽器建立多個 thread 處理任務,主要用於處理可能會使用到大量運算且並不希望影響到使用者的瀏覽體驗。建立 Web Worker。

const worker = new Worker(new URL('./workerPath.js', import.meta.url));

透過 postMessage  進行溝通。

// browser

worker.postMessage({ data: "from app.js" });
worker.addEventListener("message", (event) => {
console.log(event.data);
});

// worker

self.postMessage({ data: "hello from worker.js" });
self.addEventListener("message", (event) => {
console.log(event.data);
});

關閉 Worker。

// browser

worker.terminate();

//worker

self.close();

在 Worker 中關閉的話, Worker 會執行完 Event Loop 後才會關閉。瀏覽器中則會直接中斷關閉。

Shared Worker

Web Worker 的一種,可以在不同的頁面相同 Domain 分享訊息(某一頁面登入,其餘頁面皆改變狀態),或是節省資源(一個 Domain 只建立一個 Websocket 連線)。


avatar-img
2會員
4內容數
FE Developer
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Hello Todd 的其他內容
這是一個介紹React Text Wrap Balancer套件的文章,主要內容包括套件的使用方式,常見的實作方式和一些注意事項。文章內容較長,內容大概是在介紹套件的使用方法、使用技巧和注意事項。
這是一個介紹React Text Wrap Balancer套件的文章,主要內容包括套件的使用方式,常見的實作方式和一些注意事項。文章內容較長,內容大概是在介紹套件的使用方法、使用技巧和注意事項。
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
NCEES FE考試主要是美國工程類畢業生,大學畢業都會去考的考試,為未來成為PE做準備,只有通過考試的人才可以成為美國E.I.T.(Engineer in Traine),並通過四年的工作經歷認證,才可以考試成為PE。 <準備教材> 1.NCEES FE Reference Handbook
Thumbnail
兩年前剛出社會的時候也寫過面試心得文:2021 社會新鮮人軟體工程師求職心得分享,不知不覺兩年就過了,自己也不再是這個圈子裡最年輕的那批人,在新手的光環慢慢消失後,職涯的考驗似乎才真的開始...
嗨,我是小七! 本期是個人接觸八維後, 結合理論、各種參考資料和自己觀察的一些綜合成果。 這是談Fe/Fi的第一期,來講講不同的人格如何處理「自己的」情感。 不懂八維、Fe、Fi是什麼的小伙伴也不用擔心, 我會先用白話文大概提一下基本概念。 (=´∀`)人(´∀`=)
Thumbnail
講了這麼多,可能還是很難判斷八維功能。所以下面就提出一些各功能主要使用者,就我觀察中,可能(但不絕對)會實際表現出的具體言行的示例吧。 Fe 外傾情感 vs Fi 內傾情感
Thumbnail
【外傾情感Fe:一 個在男性身上很麻煩的功能】 Fe是個同理心高、適合傾聽、適合協調人際互動的功能。但在男人身上常常會變成一種負面困擾。
Thumbnail
那麼講到差異,外傾情感Fe,講簡單一點,就是比較注意重視人情世故和群體,而內傾情感Fi,則是比較注意重視真實感受和個體。
Thumbnail
走遍所有半路咖啡(𝐻𝑎𝑙𝑓𝑤𝑎𝑦 𝐶𝑜𝑓𝑓𝑒𝑒)分店的我,也可算是忠粉一名。每當朋友問到哪裡有好喝的咖啡,𝐻𝑎𝑙𝑓𝑤𝑎𝑦必定在我的名單上,外賣也好,堂食也好,ℎ𝑎𝑙𝑓𝑤𝑎𝑦總是能保持一貫的水準。香醇均勻、不酸,口感豐厚平順,剛好是我喜歡的口味。
Thumbnail
風花雪月二創的醍醐味就是無解的Time Loop。 《汝、死を招く者にして》的故事就從一張落在クロード房間地板上的神秘紙條開始。
Thumbnail
整本《Love Story》可以說是ディミトリ視角的クロード攻略本,雖然沒有高潮迭起的出生入死,但是不可言說的秘密、不經意洩出的想望,全都透過ディミトリ的質疑、クロード的玩笑,完整地表現出來。
Thumbnail
這樣說好了,迷因比電影還紅的最佳典範:「榮恩!我找到比魔杖還好用的東西了!」為本片打出了第一張好牌,原本以為這部片會成為Straight to DVD的那一種B級片,沒想到竟然上了院線,真是讓人又驚又喜。
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
NCEES FE考試主要是美國工程類畢業生,大學畢業都會去考的考試,為未來成為PE做準備,只有通過考試的人才可以成為美國E.I.T.(Engineer in Traine),並通過四年的工作經歷認證,才可以考試成為PE。 <準備教材> 1.NCEES FE Reference Handbook
Thumbnail
兩年前剛出社會的時候也寫過面試心得文:2021 社會新鮮人軟體工程師求職心得分享,不知不覺兩年就過了,自己也不再是這個圈子裡最年輕的那批人,在新手的光環慢慢消失後,職涯的考驗似乎才真的開始...
嗨,我是小七! 本期是個人接觸八維後, 結合理論、各種參考資料和自己觀察的一些綜合成果。 這是談Fe/Fi的第一期,來講講不同的人格如何處理「自己的」情感。 不懂八維、Fe、Fi是什麼的小伙伴也不用擔心, 我會先用白話文大概提一下基本概念。 (=´∀`)人(´∀`=)
Thumbnail
講了這麼多,可能還是很難判斷八維功能。所以下面就提出一些各功能主要使用者,就我觀察中,可能(但不絕對)會實際表現出的具體言行的示例吧。 Fe 外傾情感 vs Fi 內傾情感
Thumbnail
【外傾情感Fe:一 個在男性身上很麻煩的功能】 Fe是個同理心高、適合傾聽、適合協調人際互動的功能。但在男人身上常常會變成一種負面困擾。
Thumbnail
那麼講到差異,外傾情感Fe,講簡單一點,就是比較注意重視人情世故和群體,而內傾情感Fi,則是比較注意重視真實感受和個體。
Thumbnail
走遍所有半路咖啡(𝐻𝑎𝑙𝑓𝑤𝑎𝑦 𝐶𝑜𝑓𝑓𝑒𝑒)分店的我,也可算是忠粉一名。每當朋友問到哪裡有好喝的咖啡,𝐻𝑎𝑙𝑓𝑤𝑎𝑦必定在我的名單上,外賣也好,堂食也好,ℎ𝑎𝑙𝑓𝑤𝑎𝑦總是能保持一貫的水準。香醇均勻、不酸,口感豐厚平順,剛好是我喜歡的口味。
Thumbnail
風花雪月二創的醍醐味就是無解的Time Loop。 《汝、死を招く者にして》的故事就從一張落在クロード房間地板上的神秘紙條開始。
Thumbnail
整本《Love Story》可以說是ディミトリ視角的クロード攻略本,雖然沒有高潮迭起的出生入死,但是不可言說的秘密、不經意洩出的想望,全都透過ディミトリ的質疑、クロード的玩笑,完整地表現出來。
Thumbnail
這樣說好了,迷因比電影還紅的最佳典範:「榮恩!我找到比魔杖還好用的東西了!」為本片打出了第一張好牌,原本以為這部片會成為Straight to DVD的那一種B級片,沒想到竟然上了院線,真是讓人又驚又喜。