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 App | Native 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
2 Reload 網頁,可以看到資源已經被 cache 起來。
3 從 Network 來看,可以看到資源都是從 ServiceWorker 來
4 更改 CACHE_NAME -> Reload 網頁,此時可看到出現有遮測到變更
5 按下更新 Botton 後,網頁自動更新。
以上!