所有入門單元免費開放 · 課程內容持續更新中

變成可安裝的 App:PWA 與手機測試

替「小習慣」加上圖示、manifest.json 和 service worker,讓它可以加到手機主畫面、沒有網路也能打開。最後學會用電腦的開發者工具、Lighthouse 和真正的手機來測試。

第 9.5 堂約 13 分鐘

現在的「小習慣」已經很好用了,但每次都要打開瀏覽器、輸入網址,還會看到網址列。這堂課要讓它變成一個「真的 App」:手機主畫面上有自己的圖示,點開來全螢幕,搭捷運沒訊號時也能打卡。

還記得第二堂課說的嗎?PWA 就是在網頁 App 上多加兩樣東西。這堂課就是把它們加上去。

PWA 的三個條件

網頁 App 加上 manifest.json、service worker,在 HTTPS 網址上提供,就成為可以安裝、離線可用的 PWA

三樣東西缺一不可。開發時用 localhost 打開,瀏覽器會把它當成安全網址。

  1. HTTPS 網址:網址開頭是 https://,代表連線有加密。瀏覽器只允許安全的網址使用 service worker。開發時的 http://localhost 是例外,也被視為安全。
  2. manifest.json:App 的「身分證」,寫著名稱、圖示、主題顏色。
  3. service worker:一支在背景運作的 JavaScript,像 App 的私人管家,負責把檔案存在手機裡。

再加上圖示,我們就有了全部的材料。

步驟 1:準備圖示

PWA 至少需要兩種尺寸的 PNG 圖示:192 × 192 和 512 × 512 像素。另外建議準備一個 maskable(可裁切)版本:Android 會把圖示裁成圓形、圓角方形等不同形狀,maskable 圖示的重要內容要放在中間,四周留白,才不會被切掉。

你可以自己畫,也可以請 Claude 幫忙:

請 Claude 做 App 圖示

請替小習慣設計一個簡單的 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

service worker 站在 App 和網路中間。有網路沒網路,App 都先從手機裡的快取拿檔案。

如果你想讓 Claude 一次完成步驟 2 和步驟 3,可以這樣說:

請 Claude 把小習慣變成 PWA

請把小習慣變成可以安裝的 PWA:

  1. 建立 manifest.json:名稱小習慣、display 用 standalone、主題色 #5b4ef0、start_url 和 scope 用相對路徑 ./,圖示用 icons 資料夾裡的三個 PNG
  2. 在 index.html 加上 manifest、theme-color 和 apple-touch-icon
  3. 建立 sw.js:用快取優先策略,安裝時快取所有 App 檔案,快取名稱帶版本號,啟用時刪除舊快取
  4. 在 app.js 註冊 service worker

做完後,請告訴我怎麼用 Chrome 開發者工具確認 manifest 和 service worker 都正常,以及怎麼模擬離線。

在電腦上測試

先用本機伺服器打開 App(http://localhost:…),按 F12 打開開發者工具,切到 Application(應用程式)分頁:

  1. Manifest:看得到 App 名稱和圖示嗎?如果有錯誤或警告,會列在這裡,整段複製給 Claude。
  2. Service workers:應該看到 sw.js,狀態是 activated and is running(已啟用、執行中)。
  3. 離線測試:在 Service workers 區塊勾選「Offline」(或在 Network 分頁把網路設成離線),重新整理頁面。App 還打得開、還能打卡,就成功了。

這時候 Chrome 的網址列右邊,通常也會出現一個「安裝」的小圖示。點下去,「小習慣」就會變成電腦上的一個獨立視窗 App。

用 Lighthouse 健康檢查

開發者工具裡還有一個 Lighthouse 分頁,可以幫你的網頁做健康檢查。選擇「行動裝置」,執行分析,它會針對效能、無障礙、最佳做法等面向打分數,並列出具體的改善建議。

比較新的 Lighthouse 版本已經不再有獨立的「PWA」分數,所以安裝相關的問題請以 Application 分頁的 Manifest 檢查為準。Lighthouse 最有用的是「無障礙」的建議,例如按鈕文字顏色對比不足、圖片沒有替代文字,這些直接關係到你的朋友用起來舒不舒服。

把報告裡的問題整段貼給 Claude:「Lighthouse 說這幾項需要改善,請逐一說明原因並修正」。

在真正的手機上測試

電腦上的手機模擬只是模擬。手指的大小、陽光下的螢幕、單手操作的感覺,只有真的拿手機才知道。有兩種方式:

方法 A:同一個 Wi-Fi,用區網連線

適合:檢查版面、按鈕好不好按、文字大小。

  1. 手機和電腦連到同一個 Wi-Fi。
  2. 查電腦的區網 IP 位址:Windows 在終端機輸入 ipconfig,找「IPv4 位址」;Mac 可以輸入 ipconfig getifaddr en0。會得到類似 192.168.1.20 的數字。
  3. 啟動本機伺服器(npx serve . 或 python -m http.server 8000),在手機瀏覽器輸入 http://192.168.1.20:8000(換成你的 IP 和連接埠)。
  4. 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"
行動練習
  1. 和 Claude 一起完成圖示、manifest.json、sw.js,並在 app.js 註冊 service worker。
  2. 在電腦的開發者工具確認:Manifest 沒有錯誤、service worker 已啟用、勾選 Offline 後重新整理仍然打得開。
  3. 跑一次 Lighthouse(行動裝置模式),挑出無障礙相關的建議,請 Claude 修正至少一項。
  4. 用方法 A 在手機上打開,照著測試清單檢查版面與操作,把不順手的地方記下來。
  5. 把 sw.js 的版本號從 v1 改成 v2,在電腦上重新整理兩次,到 Application 分頁的 Cache storage 確認舊快取被刪掉了。這個動作你在下一堂課會常常用到。

讀完了嗎?

標記完成後,單元與專案的進度會自動更新(只存在這台裝置的瀏覽器)。

卡住了?讓 AI 當你的家教複製這段提問範本,貼到 Claude 或其他 AI 工具,再補上你卡住的地方。

留言與討論

卡關了、有心得,或發現內容需要更新?都歡迎留言。留言使用 GitHub 帳號登入。

留言區即將開放。現在可以先到 GitHub 討論區 發問,或看看 交流頁 的其他管道。