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 键。

触发条件

以下条件全部满足才显示,任一不满足即静默:

  1. 客户端环境(SSR 阶段不会渲染)
  2. PWA 已启用 —— 运行时判据是 runtimeConfig.public.swiftWebFramework.pwa.enabled === true,它由配置期的 swift.config.json 里 framework.pwa(布尔)派生
  3. framework.addToHomescreen.enabled !== false
  4. 当前不在 standalone 显示模式,即用户尚未把站点添加到主屏。两条判据缺一不可(fw#352):window.matchMedia('(display-mode: standalone)').matches === false 且 navigator.standalone !== true —— 后者是 iOS 专有属性,也是 iOS legacy web clip(无 linked manifest 时「添加到主屏」的产物)唯一可靠的信号,那种形态下媒体查询并不匹配
  5. 设备类型可明确判定(详见下方 UA 矩阵)
  6. 用户最近"没被记住关闭"(由 rememberFor 决定)
  7. 到点时没有模态对话框开着(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 注释/删除挂载点。

设备 / 浏览器支持矩阵

平台浏览器是否触发说明
iOSSafari✓iOS 16+ / iOS 17+ / iOS 26+
iOSChrome / Firefox / Edge✗iOS 系统限制只有 Safari 能添加到主屏
iOS微信内 / 其它 WebView✗WebView 无原生入口
AndroidChrome✓Chrome 主流入口
AndroidEdge / Samsung Internet✓同样支持
AndroidFirefox / Opera✓主流 Chromium 衍生版
AndroidWebView (; wv))✗WebView 无原生入口
AndroidUC / QQ / 微信 / 百度 / MIUI / Vivo / Oppo / 夸克 / 360 / 搜狗✗国产浏览器无 PWA 入口或体验差
桌面(macOS / Windows / Linux)任意浏览器✗桌面端不弹移动端引导

基础用法

demo 数量豁免说明:本组件的完整 UI 仅在 iOS Safari / Android Chromium 且五道闸全过时可见,桌面 docs 站无法演示更多形态(slots 自定义等需真机验证),故仅保留下方 2 个可运行 demo + 闸门探测。

挂载演示(手机浏览器可见引导)

加载中...

当前环境闸门探测(为什么不弹)

加载中...

framework 默认提供纯文字兜底,开箱即用:

<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关标签页即重置,下次进站重弹
正数 NlocalStorage(带 timestamp)关闭后 N 天内不再弹
0 / 负数不存储每次进站都弹(仅供调试)

自定义视觉

通过 CSS 变量在 applier 一侧整体调色:

变量默认值控制
--sw-add-to-homescreen-guide-bgvar(--popover)面板背景色
--sw-add-to-homescreen-guide-accentvar(--primary)步骤序号圆点底色
--sw-add-to-homescreen-guide-accent-foregroundvar(--primary-foreground)步骤序号文字色(与上一条配对,别只改一侧)
--sw-add-to-homescreen-guide-backdroprgb(0 0 0 / 45%)遮罩层颜色
--sw-add-to-homescreen-guide-step-bgvar(--muted)单条步骤的底色
--sw-add-to-homescreen-guide-radius1.25rem顶部圆角
--sw-add-to-homescreen-guide-z1100z-index
--sw-add-to-homescreen-guide-close-bgvar(--muted)关闭钮底色(淡圆底,让它读起来像按钮)
--sw-add-to-homescreen-guide-close-hover-bgvar(--accent)关闭钮 hover 底色(仅 hover: hover 设备)
--sw-add-to-homescreen-guide-shadow0 -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

名称类型必填默认值说明
delaynumber2000mount 后多久弹出(毫秒)。优先级:prop > runtimeConfig > 2000
rememberFor'session' | PeriodSpec'session'记忆关闭状态多久。'session' = sessionStorage,关标签即重置;'Nm'·'Nh'·'Nd' = localStorage 存 timestamp,N 天内不再弹;0/负数 = 不记忆

Slots

插槽名默认渲染说明
iconmanifest 的 /icons/icon-192.png(加载失败则隐藏)应用图标
title站点名(project.title),无则回退"保存到主屏幕"弹窗标题
ios-instructionsiOS 三步(前两步带内置线性 SVG 图标)iOS Safari 操作步骤内容
android-instructionsAndroid 三步(前两步带内置线性 SVG 图标)Android Chromium 操作步骤内容

故障排查

引导没弹?按以下顺序检查:

  1. swift.config.json 里 framework.pwa 是否为 true(布尔,不是对象)?dev console 应该有相应 warn 帮助定位。写成 { "enabled": true } 会静默关闭 PWA,见上「项目级配置」
  2. 是否在客户端首次访问?同一标签页刷新已弹过的不再弹(除非 rememberFor 设为 0)
  3. 当前 UA 是否在支持矩阵里?iOS Chrome / 国产浏览器 / 桌面端都不弹
  4. 是否已经"添加到主屏幕"过?iOS Safari / Android Chrome 添加后 UA 切到 standalone 模式,组件主动跳过
  5. addToHomescreen.enabled 是否被显式设为 false?检查 swift.config.json
  6. rememberFor: 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 模拟未必能触发对应分支。