SwAddToHomescreenGuide
H5 行動端使用者首次造訪 PWA 專案時,從螢幕底部滑入一張非阻斷式資訊卡片,引導使用者把站點儲存到主畫面。 iOS Safari 和 Android 主流 Chromium 瀏覽器(Chrome / Edge / Samsung Internet / Firefox / Opera)分別給出對應步驟; 其它環境(桌面 / WebView / iOS 非 Safari / 區域型瀏覽器等)一律靜默不彈出,避免誤導。
元件使用 <Teleport to="body"> + <Transition> 渲染為底部 sheet,
帶半透明遮罩(--sw-add-to-homescreen-guide-backdrop,預設 rgb(0 0 0 / 45%)),但不鎖捲動 / 不搶焦;
關閉方式三選一:右上角 X 按鈕 / 點遮罩 / Escape 鍵。
觸發條件
以下條件全部滿足才顯示:
- 客戶端環境(SSR 不會渲染)
- PWA 已啟用 —— 執行時判據是
runtimeConfig.public.swiftWebFramework.pwa.enabled === true,它由設定期的swift.config.json裡framework.pwa(布林)派生 framework.addToHomescreen.enabled !== false- 不在 standalone 顯示模式,即使用者尚未把站點加到主畫面。兩條判據缺一不可(fw#352):
window.matchMedia('(display-mode: standalone)').matches === false且navigator.standalone !== true—— 後者是 iOS 專有屬性,也是 iOS legacy web clip(無 linked manifest 時「加入主畫面」的產物)唯一可靠的訊號,那種形態下媒體查詢並不匹配 - 設備類型可明確判定(詳見下方 UA 矩陣)
- 使用者最近"沒被記住關閉"(由
rememberFor決定) - 到點時沒有模態對話框開著(fw#387):判據是
document.body.style.pointerEvents === 'none'——SwModal 底層的 reka-ui Dialog 開啟期間會把 body 設成 pointer-events: none,引導層就算 z-index 更高、看得見,X 與遮罩也點不動。命中則每 500ms 複查,對話框關掉後再彈。業務自帶的年齡確認 / 公告彈窗與引導同屏時尤其常見
PWA 未啟用時的警告:dev 模式下,若元件已掛載但執行時
pwa.enabled === false, 會在 console 輸出一則 warn,提醒開發者要嘛啟用 PWA、要嘛從app.vue註解 / 刪除掛載點。
設備 / 瀏覽器支援矩陣
| 平台 | 瀏覽器 | 是否觸發 | 說明 |
|---|---|---|---|
| iOS | Safari | ✓ | iOS 16+ / 17+ / 26+ |
| iOS | Chrome / Firefox / Edge | ✗ | iOS 系統限制只有 Safari 能加到主畫面 |
| iOS | LINE / 其它 WebView | ✗ | WebView 無原生入口 |
| Android | Chrome | ✓ | Chrome 主流入口 |
| Android | Edge / Samsung Internet | ✓ | 同樣支援 |
| Android | Firefox / Opera | ✓ | 主流 Chromium 衍生版 |
| Android | WebView (; wv)) | ✗ | WebView 無原生入口 |
| Android | UC / QQ / WeChat / 百度 / MIUI / Vivo / Oppo / Quark / 360 / Sogou | ✗ | 區域型瀏覽器無 PWA 入口或體驗差 |
| 桌面(macOS / Windows / Linux) | 任意瀏覽器 | ✗ | 桌面端不彈行動端引導 |
基本用法
demo 數量豁免說明:本元件的完整 UI 僅在 iOS Safari / Android Chromium 且五道閘全過時可見,桌面 docs 站無法演示更多形態(slots 自訂等需真機驗證),故僅保留下方 2 個可運行 demo + 閘門探測。
掛載演示(手機瀏覽器可見引導)
當前環境閘門探測(為什麼不彈)
framework 預設提供純文字 fallback,開箱即用:
<template>
<NuxtLayout><NuxtPage /></NuxtLayout>
<SwModalHost />
<SwToast />
<SwAddToHomescreenGuide />
</template>
掛載到 app.vue 與 <SwModalHost />、<SwToast /> 同層即可。framework 在 mount 後 2 秒(預設)滑入引導,
包含 iOS 或 Android 的兩步圖文。
注入圖示庫
framework 不內建圖示 SVG(遵守"不引圖示庫"規範);推薦 applier 透過 slot 注入圖示庫做更豐富的視覺。
以下為 @phosphor-icons/vue 真實範例:
<script setup lang="ts">
import { PhShareNetwork, PhDotsThreeVertical, PhPlusSquare } from '@phosphor-icons/vue'
</script>
<template>
<SwAddToHomescreenGuide>
<template #ios-instructions>
<li>
<PhShareNetwork :size="20" weight="regular" />
點擊 Safari 工具列的<strong>「分享」</strong>按鈕
</li>
<li>
<PhPlusSquare :size="20" weight="regular" />
選擇<strong>「加入主畫面」</strong>並確認
</li>
</template>
<template #android-instructions>
<li>
<PhDotsThreeVertical :size="20" weight="regular" />
點擊瀏覽器右上角的<strong>選單</strong>
</li>
<li>
<PhPlusSquare :size="20" weight="regular" />
選擇<strong>「加入主畫面」</strong>並確認
</li>
</template>
</SwAddToHomescreenGuide>
</template>
控制彈出頻率
預設每個瀏覽器會話只彈一次(關閉分頁後重置)。如希望"使用者關閉後 N 天內不再打擾",使用 rememberFor prop:
<!-- 一週內不再彈 -->
<SwAddToHomescreenGuide remember-for="7d" />
<!-- 一天內不再彈 -->
<SwAddToHomescreenGuide remember-for="1d" />
<!-- 實質永久(100 年) -->
<SwAddToHomescreenGuide remember-for="36500d" />
rememberFor 值 | 儲存後端 | 行為 |
|---|---|---|
'session'(預設) | sessionStorage | 關分頁即重置,下次造訪重彈 |
| 正數 N | localStorage(帶 timestamp) | 關閉後 N 天內不再彈 |
| 0 / 負數 | 不儲存 | 每次造訪都彈(僅供除錯) |
自訂視覺
透過 CSS 變數在 applier 一側整體調色:
| 變數 | 預設值 | 控制 |
|---|---|---|
--sw-add-to-homescreen-guide-bg | var(--popover) | 面板背景色 |
--sw-add-to-homescreen-guide-accent | var(--primary) | 步驟序號圓點底色 |
--sw-add-to-homescreen-guide-accent-foreground | var(--primary-foreground) | 步驟序號文字色(與上一條配對,別只改一側) |
--sw-add-to-homescreen-guide-backdrop | rgb(0 0 0 / 45%) | 遮罩層顏色 |
--sw-add-to-homescreen-guide-step-bg | var(--muted) | 單條步驟的底色 |
--sw-add-to-homescreen-guide-radius | 1.25rem | 頂部圓角 |
--sw-add-to-homescreen-guide-z | 1100 | z-index |
--sw-add-to-homescreen-guide-close-bg | var(--muted) | 關閉鈕底色(淡圓底,讓它讀起來像按鈕) |
--sw-add-to-homescreen-guide-close-hover-bg | var(--accent) | 關閉鈕 hover 底色(僅 hover: hover 裝置) |
--sw-add-to-homescreen-guide-shadow | 0 -8px 32px rgb(0 0 0 / 12%) | 面板投影。向上投影,不套 --shadow-lg(那三檔按向下投影設計) |
類別鉤子 .sw-add-to-homescreen-guide(portal 到 body 的根 div)允許深度覆蓋佈局。元件由於 Teleport 到 body,
scoped CSS 不可達,需用非 scoped <style> 覆蓋。
<style>
.sw-add-to-homescreen-guide {
--sw-add-to-homescreen-guide-accent: #ff6b00;
--sw-add-to-homescreen-guide-radius: 1.25rem;
border: 4px solid #000 !important;
box-shadow: 6px 6px 0 0 #000 !important;
}
</style>
專案級設定
除元件 prop 外,可透過 swift.config.json 設定專案級預設(透過 framework module 的 runtimeConfig 傳遞):
{ "framework": { "pwa": true } }
🔴 寫成
{ "pwa": { "enabled": true } }會靜默關閉 PWA。framework 的判據是嚴格=== true(module.ts),物件形態判偽 ⇒@vite-pwa/nuxt整個不裝, Service Worker / manifest / 主畫面鏈路全都沒有 —— 效果與寫"pwa": false完全相同, 且全程無報錯。sw config set framework.pwa也會拒絕物件形態(它在 CLI 的布林鍵名單裡)。
引導浮層的行為寫在 nuxt.config.ts(本項不進 swift.config.json,CLI 不管它 ——
它是 framework 的元件行為,不是 manifest 內容):
swiftConfig: {
...swiftConfig.framework,
addToHomescreen: { delay: 3000, rememberFor: '7d' }, // enabled 省略 = 跟随 framework.pwa
},
enabled可省略,省略時跟隨framework.pwa(與不傳整個addToHomescreen同義), 所以只想改延遲寫{ delay: 3000 }即可。顯式enabled: false可在 PWA 開著時單獨關掉引導。
優先順序:元件 prop > nuxt.config.ts 的 swiftConfig > 內建預設('session' / 2000ms)。
如希望完全停用引導(保留 PWA 但不彈引導):
swiftConfig: { ...swiftConfig.framework, addToHomescreen: false },
Props
| 名稱 | 類型 | 必填 | 預設值 | 說明 |
|---|---|---|---|---|
delay | number | 2000 | mount 後多久彈出(毫秒)。優先順序:prop > runtimeConfig > 2000 | |
rememberFor | 'session' | PeriodSpec | 'session' | 記憶關閉狀態多久。'session' = sessionStorage;'Nm'·'Nh'·'Nd' = N 分·時·天內不再彈;'0d' = 不記憶 |
Slots
| 插槽名 | 預設渲染 | 說明 |
|---|---|---|
icon | manifest 的 /icons/icon-192.png(載入失敗則隱藏) | 應用圖示 |
title | 站點名(project.title),無則回退「儲存到主畫面」 | 彈窗標題 |
ios-instructions | iOS 三步(前兩步帶內建線性 SVG 圖示) | iOS Safari 操作步驟內容 |
android-instructions | Android 三步(前兩步帶內建線性 SVG 圖示) | Android Chromium 操作步驟內容 |
疑難排解
引導沒彈?依下列順序檢查:
swift.config.json裡framework.pwa是否為true(布林,不是物件)?dev console 應該有對應 warn 幫助定位。寫成{ "enabled": true }會靜默關閉 PWA,見上「專案級設定」- 是否在客戶端首次造訪?同分頁刷新已彈過的不再彈(除非
rememberFor: 0) - 目前 UA 是否在支援矩陣裡?iOS Chrome / 區域型瀏覽器 / 桌面端都不彈
- 是否已"加到主畫面"過?iOS Safari / Android Chrome 加到主畫面後 UA 切到 standalone 模式,元件主動跳過
addToHomescreen.enabled是否被顯式設為false?檢查swift.config.jsonrememberFor: N模式下,localStorage 的sw:a2hs:dismissedAt是否還在 N 天視窗內? 清瀏覽器儲存或調小rememberFor重新觸發
沒有 live demo:本元件依賴行動端 UA + 真實 PWA 設定,桌面瀏覽器無法觸發,文檔站不提供 DemoBox。 請在真機或 Chrome DevTools「Toggle device toolbar」模擬行動裝置 UA 自行驗證。
注意事項
- 「怎麼都不彈」先按 5 道靜默閘排查(Rule UAH-001)—— 多數條件不滿足時靜默 return、無任何提示(僅「PWA 未啟用」有 dev warn):① PWA 未啟用 ②
addToHomescreen.enabled === false③ 已在 standalone(在已安裝的 PWA 視窗裡除錯就永遠不彈)④ 裝置非 iOS Safari / Android Chromium(含糊裝置一律靜默)⑤ 處於已記憶的靜默期內。 rememberFor是三態,最容易選錯 —— 正數N= N 天內不再彈(localStorage 持久);'session'= 關分頁即重置;0或負數 = 每次都彈(僅測試用)。想「每次進站演示」用了'session'會變成一個工作階段只彈一次;想「永久只彈一次」用'session'則關頁又彈。常用值是7。- iOS 與 Android 的引導文案 / 圖示不同 —— 元件按裝置分支算繪,除錯時注意 UA 模擬未必能觸發對應分支。