變成可安裝的 App:PWA 與手機測試
替「小習慣」加上圖示、manifest.json 和 service worker,讓它可以加到手機主畫面、沒有網路也能打開。最後學會用電腦的開發者工具、Lighthouse 和真正的手機來測試。
現在的「小習慣」已經很好用了,但每次都要打開瀏覽器、輸入網址,還會看到網址列。這堂課要讓它變成一個「真的 App」:手機主畫面上有自己的圖示,點開來全螢幕,搭捷運沒訊號時也能打卡。
還記得第二堂課說的嗎?PWA 就是在網頁 App 上多加兩樣東西。這堂課就是把它們加上去。
PWA 的三個條件
三樣東西缺一不可。開發時用 localhost 打開,瀏覽器會把它當成安全網址。
- HTTPS 網址:網址開頭是
https://,代表連線有加密。瀏覽器只允許安全的網址使用 service worker。開發時的http://localhost是例外,也被視為安全。 - manifest.json:App 的「身分證」,寫著名稱、圖示、主題顏色。
- service worker:一支在背景運作的 JavaScript,像 App 的私人管家,負責把檔案存在手機裡。
再加上圖示,我們就有了全部的材料。
步驟 1:準備圖示
PWA 至少需要兩種尺寸的 PNG 圖示:192 × 192 和 512 × 512 像素。另外建議準備一個 maskable(可裁切)版本:Android 會把圖示裁成圓形、圓角方形等不同形狀,maskable 圖示的重要內容要放在中間,四周留白,才不會被切掉。
你可以自己畫,也可以請 Claude 幫忙:
請替小習慣設計一個簡單的 App 圖示,存成 icons/icon.svg:紫色(#5b4ef0)圓角方形背景,中間一個白色打勾符號,風格簡潔、在手機主畫面上縮小也看得清楚。
接著幫我把它轉成三個 PNG:icons/icon-192.png、icons/icon-512.png,以及 icons/icon-maskable-512.png(maskable 版本背景要填滿整個正方形,打勾縮小放在中間的安全範圍內)。請告訴我你用什麼方法轉檔,如果需要安裝工具,先問過我。
Claude 可能會寫一個小腳本來轉檔,或建議你用繪圖軟體、線上的 PWA 圖示產生器匯出。哪種都可以,重點是最後 icons 資料夾裡有這三個檔案。想確認 maskable 圖示會不會被切到,可以搜尋「maskable icon editor」,用線上工具預覽各種裁切形狀。
步驟 2:manifest.json
在專案根目錄建立 manifest.json:
{
"name": "小習慣",
"short_name": "小習慣",
"description": "每天點一下,慢慢養成好習慣",
"start_url": "./",
"scope": "./",
"display": "standalone",
"background_color": "#f6f6fb",
"theme_color": "#5b4ef0",
"lang": "zh-Hant-TW",
"icons": [
{ "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
每個欄位的意思:
| 欄位 | 意思 |
|---|---|
name、short_name |
App 的全名和主畫面上顯示的短名稱 |
start_url |
從主畫面點開時,要打開哪一頁 |
scope |
哪些網址算是這個 App 的範圍 |
display |
standalone 代表打開時隱藏網址列,看起來像一般 App |
background_color |
App 啟動畫面的背景色 |
theme_color |
手機狀態列、標題列的顏色 |
icons |
各種尺寸的圖示 |
start_url 和 scope 用 ./(相對路徑),而不是 /。這樣不管你之後把 App 放在網站的根目錄,還是 GitHub Pages 的 /tiny-habits/ 子資料夾,都能正常運作。
接著在 index.html 的 head 裡加上這三行:
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#5b4ef0">
<link rel="apple-touch-icon" href="icons/icon-192.png">
最後一行是給 iPhone 用的。iPhone 加到主畫面時,主要看這個標籤來決定圖示。
步驟 3:service worker
在專案根目錄建立 sw.js。注意它要放在根目錄,因為 service worker 只能管理它所在資料夾(以及子資料夾)裡的網址。
// 每次改版,記得把版本號往上加,手機才會換成新檔案
const CACHE = 'tiny-habits-v1';
const FILES = [
'./',
'./index.html',
'./style.css',
'./app.js',
'./manifest.json',
'./icons/icon-192.png',
'./icons/icon-512.png',
];
// 安裝:把 App 需要的檔案先存進快取
self.addEventListener('install', (event) => {
event.waitUntil(caches.open(CACHE).then((cache) => cache.addAll(FILES)));
self.skipWaiting();
});
// 啟用:刪掉舊版本的快取
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((key) => key !== CACHE).map((key) => caches.delete(key)))
)
);
self.clients.claim();
});
// 取用:快取有就用快取,沒有才上網抓
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then((cached) => cached || fetch(event.request))
);
});
然後在 app.js 的最後加上註冊的程式碼,告訴瀏覽器「請啟用這位管家」:
// ---------- PWA:註冊 service worker ----------
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js');
}
這支 service worker 的策略叫做快取優先(cache first):
- 第一次打開時(
install),把FILES清單裡的檔案全部存進手機的快取。 - 之後每次 App 要讀檔案時(
fetch),先看快取裡有沒有,有就直接用,完全不需要網路。 - 新版本啟用時(
activate),把舊版本的快取清掉。
service worker 站在 App 和網路中間。有網路沒網路,App 都先從手機裡的快取拿檔案。
如果你想讓 Claude 一次完成步驟 2 和步驟 3,可以這樣說:
請把小習慣變成可以安裝的 PWA:
- 建立 manifest.json:名稱小習慣、display 用 standalone、主題色 #5b4ef0、start_url 和 scope 用相對路徑 ./,圖示用 icons 資料夾裡的三個 PNG
- 在 index.html 加上 manifest、theme-color 和 apple-touch-icon
- 建立 sw.js:用快取優先策略,安裝時快取所有 App 檔案,快取名稱帶版本號,啟用時刪除舊快取
- 在 app.js 註冊 service worker
做完後,請告訴我怎麼用 Chrome 開發者工具確認 manifest 和 service worker 都正常,以及怎麼模擬離線。
在電腦上測試
先用本機伺服器打開 App(http://localhost:…),按 F12 打開開發者工具,切到 Application(應用程式)分頁:
- Manifest:看得到 App 名稱和圖示嗎?如果有錯誤或警告,會列在這裡,整段複製給 Claude。
- Service workers:應該看到
sw.js,狀態是 activated and is running(已啟用、執行中)。 - 離線測試:在 Service workers 區塊勾選「Offline」(或在 Network 分頁把網路設成離線),重新整理頁面。App 還打得開、還能打卡,就成功了。
這時候 Chrome 的網址列右邊,通常也會出現一個「安裝」的小圖示。點下去,「小習慣」就會變成電腦上的一個獨立視窗 App。
用 Lighthouse 健康檢查
開發者工具裡還有一個 Lighthouse 分頁,可以幫你的網頁做健康檢查。選擇「行動裝置」,執行分析,它會針對效能、無障礙、最佳做法等面向打分數,並列出具體的改善建議。
比較新的 Lighthouse 版本已經不再有獨立的「PWA」分數,所以安裝相關的問題請以 Application 分頁的 Manifest 檢查為準。Lighthouse 最有用的是「無障礙」的建議,例如按鈕文字顏色對比不足、圖片沒有替代文字,這些直接關係到你的朋友用起來舒不舒服。
把報告裡的問題整段貼給 Claude:「Lighthouse 說這幾項需要改善,請逐一說明原因並修正」。
在真正的手機上測試
電腦上的手機模擬只是模擬。手指的大小、陽光下的螢幕、單手操作的感覺,只有真的拿手機才知道。有兩種方式:
方法 A:同一個 Wi-Fi,用區網連線
適合:檢查版面、按鈕好不好按、文字大小。
- 手機和電腦連到同一個 Wi-Fi。
- 查電腦的區網 IP 位址:Windows 在終端機輸入
ipconfig,找「IPv4 位址」;Mac 可以輸入ipconfig getifaddr en0。會得到類似192.168.1.20的數字。 - 啟動本機伺服器(
npx serve .或python -m http.server 8000),在手機瀏覽器輸入http://192.168.1.20:8000(換成你的 IP 和連接埠)。 - Windows 可能會跳出防火牆詢問,只允許「私人網路」即可。
限制:這個網址是 http:// 而不是 HTTPS,手機瀏覽器不會啟用 service worker,所以無法測試離線和真正的安裝。打不開的話,請 Claude 幫你檢查伺服器和防火牆設定。
方法 B:放到 HTTPS 網址上
適合:測試安裝到主畫面、離線使用。
下一堂課我們會正式把 App 部署到網路上,拿到一個 HTTPS 網址。你可以先跳去看下一堂的部署步驟,部署完再回來做這段測試。
拿到 HTTPS 網址後:
- Android(Chrome):打開網址,瀏覽器可能會主動提示安裝;沒有的話,從右上角選單找「安裝應用程式」或「加到主畫面」。
- iPhone(Safari):打開網址,點下方的「分享」按鈕,選「加入主畫面」。
安裝好之後,從主畫面點開「小習慣」,然後開啟飛航模式,關掉 App 再重新打開。還能打開、還能打卡,你的 PWA 就完成了。
手機測試清單
| 檢查項目 | 通過的樣子 |
|---|---|
| 主畫面圖示 | 圖示清楚、沒被裁掉、名稱顯示「小習慣」 |
| 全螢幕 | 點開後沒有瀏覽器網址列 |
| 離線 | 飛航模式下能打開、能打卡 |
| 資料保留 | 關掉 App 再打開,打卡紀錄還在 |
| 單手操作 | 拇指按得到打卡按鈕,不會誤觸刪除 |
| 輸入 | 點輸入框時畫面不會自動放大 |
| 備份 | 匯出的檔案找得到,也能匯入回來 |
git add .
git commit -m "加入 manifest、service worker 與圖示,變成 PWA"
- 和 Claude 一起完成圖示、manifest.json、sw.js,並在 app.js 註冊 service worker。
- 在電腦的開發者工具確認:Manifest 沒有錯誤、service worker 已啟用、勾選 Offline 後重新整理仍然打得開。
- 跑一次 Lighthouse(行動裝置模式),挑出無障礙相關的建議,請 Claude 修正至少一項。
- 用方法 A 在手機上打開,照著測試清單檢查版面與操作,把不順手的地方記下來。
- 把
sw.js的版本號從 v1 改成 v2,在電腦上重新整理兩次,到 Application 分頁的 Cache storage 確認舊快取被刪掉了。這個動作你在下一堂課會常常用到。
讀完了嗎?
標記完成後,單元與專案的進度會自動更新(只存在這台裝置的瀏覽器)。
留言與討論
卡關了、有心得,或發現內容需要更新?都歡迎留言。留言使用 GitHub 帳號登入。