Home Service Worker of PWA
Post
Cancel

Service Worker of PWA

Web app

Progressive Web App (PWA) 的核心技術 Service worker,可讓 Web App 的使用體驗,更加接近 Native App。以下我們來看一下什麼是 Web App 吧!

Web App(網頁應用程式)是一種透過網頁技術建構的應用程式,用戶可以透過網頁瀏覽器直接訪問和使用,而不需要下載和安裝傳統的應用程式。Web App 的外觀和操作體驗與原生應用(Native App)相似,通常也支援多種設備(如電腦、平板、手機等)。

Web App 適合資源有限、時間緊迫的項目,尤其是需要跨平台兼容的應用。而 Native App 更適合需要高性能和豐富功能的應用,如遊戲、AR 應用等。

特點Web AppNative App
開發成本單一代碼,跨平台多代碼庫,需分別開發
性能一般不如原生高效且流暢,支援硬體加速
用戶體驗近似原生體驗,但有限制完整的原生體驗,系統整合更好
分發方式瀏覽器直接訪問通過應用商店發佈
離線支持PWA 提供有限的離線支持更好的離線支持

PWA

為了讓 Web App 的體驗更接近 Native App,Progressive Web App(PWA)的概念大約在2015年左右開始被導入,當時由Google的工程師們提出了這一想法。隨著時間的推移,許多瀏覽器開始支持 PWA 的相關技術,使其在開發社區中越來越受到關注和應用。

大多數主流瀏覽器都支援 Progressive Web App(PWA)及其核心技術之一的 Service Worker。PWA 的一個核心組件是 Service Worker,它是一種 JavaScript 檔案,能控制網頁的緩存策略,讓應用程式在無網路連線或網路較差的情況下也能正常使用。

以下是支援 PWA Service Worker 的主要瀏覽器:Google Chrome、Mozilla Firefox、Microsoft Edge、Safari、Opera

Service Worker: 要透過 Service Worker 實現 PWA 的功能,需將 Service Worker 檔案註冊並設計好其行為邏輯。Service Worker 在 PWA 中的作用主要是攔截和處理網路請求,緩存資源,並在沒有網路的情況下提供離線存取

codes

以下示範如何使用 service worker 來處理版本更新問題。

為了方便 demo,我們把 ui 和註冊 Service Worker 的邏輯寫在一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
</head>

<body>

    <img src="A.png" />
    <img src="B.png" />
    <img src="C.png" />

    <script type="text/javascript">
        // app.js
        const $notification = document.createElement('div');
        $notification.id = 'notification';
        $notification.style.position = 'fixed';
        $notification.style.bottom = '20px';
        $notification.style.right = '20px';
        $notification.style.backgroundColor = 'black';
        $notification.style.color = 'white';
        $notification.style.padding = '10px';
        $notification.style.borderRadius = '5px';
        $notification.style.display = 'none';
        $notification.innerHTML = `
  New version available!
  <button id="btn-refresh" style="margin-left: 10px;">Refresh</button>`;

        document.body.appendChild($notification);

        const $btnRefresh = document.getElementById('btn-refresh');

        // 註冊 Service Worker
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('/service-worker.js')
                .then(registration => {
                    // 檢查是否有等待中的 Service Worker
                    if (registration.waiting) {
                        showUpdateNotification();
                    }

                    // 當發現有新的 Service Worker 更新時觸發
                    registration.addEventListener('updatefound', () => {
                        const newWorker = registration.installing;
                        newWorker.addEventListener('statechange', () => {
                            if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
                                showUpdateNotification();
                            }
                        });
                    });

                    // 點擊刷新按鈕時,發送訊息使等待中的 Service Worker 成為活躍的
                    $btnRefresh.addEventListener('click', () => {
                        registration.waiting.postMessage({ action: 'SKIP_WAITING' });
                        $notification.style.display = 'none';
                    });
                });

            // 檢測 Service Worker 控制器變化,並重新載入頁面
            let refreshing = false;
            navigator.serviceWorker.addEventListener('controllerchange', () => {
                if (!refreshing) {
                    window.location.reload();
                    refreshing = true;
                }
            });
        }

        function showUpdateNotification() {
            $notification.style.display = 'block';
        }

    </script>

</body>

</html>

service-worker.js

CACHE_NAME 大家可以自行修改,reload 網頁就會偵測到有更新,會重載。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
const CACHE_NAME = 'my-cache-v1';
const CACHE_ASSETS = [
    '/'
];

// 安裝 Service Worker 並建立快取
self.addEventListener('install', (event) => {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then((cache) => {
                console.log('緩存資源中...');
                return cache.addAll(CACHE_ASSETS);
            })
    );
});

// 攔截請求並提供離線功能
self.addEventListener('fetch', (event) => {
    event.respondWith(
        caches.match(event.request)
            .then((response) => {
                // 若找到快取,直接返回
                console.log('攔截請求並提供離線功能');
                return response || fetch(event.request)
                    .then((fetchResponse) => {
                        // 同時將新資源快取
                        return caches.open(CACHE_NAME)
                            .then((cache) => {
                                cache.put(event.request, fetchResponse.clone());
                                return fetchResponse;
                            });
                    });
            })
            .catch(() => caches.match('/fallback.html')) // 無法取得時提供替代頁面
    );
});

// 自動更新(自動刪除上一個 cache 資料)
self.addEventListener('activate', (event) => {
    const cacheWhitelist = [CACHE_NAME];
    event.waitUntil(
        caches.keys().then((cacheNames) => {
            console.log('自動更新');
            return Promise.all(
                cacheNames.map((cacheName) => {
                    if (!cacheWhitelist.includes(cacheName)) {
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
});

// 接收來自主線程的訊息,處理 SKIP_WAITING
self.addEventListener('message', (event) => {
    if (event.data.action === 'SKIP_WAITING') {
        self.skipWaiting();
    }
});

示範

大家把上述的 code 找個 web server run 起來,不知道用什麼的話, 推薦使用 nginx。

1 開啟 DevTools -> Application -> Storage -> Cache Storage 可以看到我們自訂義的 CACHE_NAME

Desktop View

2 Reload 網頁,可以看到資源已經被 cache 起來。

Desktop View

3 從 Network 來看,可以看到資源都是從 ServiceWorker 來

Desktop View

4 更改 CACHE_NAME -> Reload 網頁,此時可看到出現有遮測到變更

Desktop View

5 按下更新 Botton 後,網頁自動更新。

Desktop View

以上!

☝ツ☝

This post is licensed under CC BY 4.0 by the author.

👈 ツ 👍