主题
移动端适配与 PWA
2025 年全球移动端流量占比超过 60%,但很多 Web 应用仍然是"桌面优先、移动凑合"。移动端适配不仅仅是"屏幕变小"——触摸交互、网络环境、性能预算、离线能力都与桌面端截然不同。PWA(Progressive Web App)更进一步,让 Web 应用具备原生 App 的体验——安装到主屏幕、离线访问、推送通知。本章系统讲解 Next.js 的移动端适配策略和 PWA 实现。
1. 移动端适配基础
1.1 移动端与桌面端的核心差异
| 维度 | 桌面端 | 移动端 |
|---|---|---|
| 输入方式 | 鼠标 + 键盘 | 触摸 + 手势 |
| 屏幕尺寸 | 1280-2560px | 320-430px |
| 网络 | 稳定宽带 | 4G/5G(可能不稳定) |
| 性能 | 高端 CPU/GPU | 中低端处理器 |
| 注意力 | 长时间深度使用 | 碎片化短时使用 |
| 上下文 | 办公室/家 | 地铁/户外/走路 |
1.2 Viewport 配置
Next.js App Router 中配置 viewport:
ts
// app/layout.tsx
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1, // 禁止用户缩放(表单场景避免 iOS 自动缩放)
userScalable: false,
themeColor: '#ffffff',
}| 配置项 | 推荐值 | 说明 |
|---|---|---|
width | device-width | 视口宽度等于设备宽度 |
initialScale | 1 | 初始缩放 100% |
maximumScale | 看场景 | 内容站允许缩放,App 型可禁止 |
themeColor | 品牌色 | 浏览器工具栏颜色 |
1.3 触摸交互设计
| 设计原则 | 说明 |
|---|---|
| 点击区域 ≥ 44px | Apple HIG 推荐的最小触摸目标 |
| 避免 hover 依赖 | 移动端没有 hover 状态 |
| 手势友好 | 上下滑动浏览、左右滑动切换 |
| 底部导航 | 拇指操作区在屏幕下半部分 |
| 避免双击缩放延迟 | touch-action: manipulation |
| 安全区域 | 适配 iPhone 刘海和底部条 |
1.4 安全区域适配
现代手机有刘海、挖孔、圆角等异形屏幕,需要使用 env() 函数适配:
css
.bottom-nav {
padding-bottom: env(safe-area-inset-bottom, 0px);
}
.header {
padding-top: env(safe-area-inset-top, 0px);
}Tailwind v4 中可以用 pb-[env(safe-area-inset-bottom)] 内联使用。
2. 移动端性能优化
2.1 移动端性能预算
| 指标 | 桌面目标 | 移动目标 | 说明 |
|---|---|---|---|
| LCP | < 2.5s | < 2.5s | 移动端网络慢,需要更多优化 |
| INP | < 200ms | < 200ms | 移动端 CPU 慢,JS 执行更耗时 |
| JS Bundle | < 200KB | < 150KB | 移动端解析 JS 更慢 |
| 首屏图片 | 优化 | WebP/AVIF + 适配尺寸 | 不要加载桌面尺寸的图片 |
2.2 响应式图片
不同设备应该加载不同尺寸的图片:
| 设备 | 屏幕宽度 | 图片宽度 | 预估大小 |
|---|---|---|---|
| 手机 | 375px | 375px | ~30KB |
| 平板 | 768px | 768px | ~80KB |
| 桌面 | 1280px | 1280px | ~150KB |
Next.js <Image> 组件自动处理:
- 根据设备 DPR 生成
srcset - 懒加载视口外的图片
- 自动转换为 WebP/AVIF
- 根据容器宽度选择最优尺寸
2.3 移动端特有的性能优化
| 优化 | 说明 |
|---|---|
| 减少 JS 执行量 | Server Components 把逻辑移到服务端 |
| 骨架屏 | Suspense fallback 替代白屏等待 |
| 预连接 | preconnect 到 API 域名 |
| 字体优化 | font-display: swap + next/font |
| 延迟加载非关键资源 | dynamic() + loading="lazy" |
| 减少动画 | prefers-reduced-motion 检测 |
3. PWA 基础
3.1 什么是 PWA
PWA 是一种让 Web 应用具备原生 App 体验的技术集合:
| 能力 | 原生 App | Web | PWA |
|---|---|---|---|
| 安装到主屏幕 | ✅ | ❌ | ✅ |
| 离线访问 | ✅ | ❌ | ✅ |
| 推送通知 | ✅ | ❌ | ✅ |
| 全屏模式 | ✅ | ❌ | ✅ |
| 自动更新 | ✅ | ✅ | ✅ |
| 应用商店 | ✅ | ❌ | ⚠️ 部分 |
| 跨平台 | ❌ | ✅ | ✅ |
| 无需安装 | ❌ | ✅ | ✅ |
| SEO | ❌ | ✅ | ✅ |
3.2 PWA 的三大要素
| 要素 | 作用 | 必须? |
|---|---|---|
| HTTPS | 安全环境 | ✅ 必须 |
| Web App Manifest | 定义应用名称、图标、启动行为 | ✅ 必须 |
| Service Worker | 离线缓存、后台同步、推送通知 | ✅ 核心 |
3.3 PWA 在 2025 年的现状
| 平台 | 支持程度 | 限制 |
|---|---|---|
| Android Chrome | ⭐⭐⭐⭐⭐ | 完整支持 |
| iOS Safari | ⭐⭐⭐ | 推送通知支持(iOS 16.4+),但仍有限制 |
| Windows Edge | ⭐⭐⭐⭐⭐ | 完整支持 |
| macOS Safari | ⭐⭐⭐⭐ | Sonoma 开始支持 |
iOS 的限制:
- Storage 限制(50MB 缓存)
- 后台执行受限
- 推送通知需要用户从主屏幕打开
- 没有完整的 Web App Manifest 支持
4. Web App Manifest
4.1 Manifest 配置
Next.js App Router 中可以通过 app/manifest.ts 生成:
| 字段 | 说明 | 示例 |
|---|---|---|
name | 完整应用名 | "My App" |
short_name | 简短名称(主屏幕图标下方) | "App" |
start_url | 启动 URL | "/" |
display | 显示模式 | "standalone" |
background_color | 启动画面背景色 | "#ffffff" |
theme_color | 主题色 | "#3b82f6" |
icons | 应用图标数组 | 多个尺寸 |
4.2 显示模式
| 模式 | 效果 | 适用场景 |
|---|---|---|
browser | 普通浏览器 | 不推荐 |
minimal-ui | 最小化浏览器 UI | 内容网站 |
standalone | 无浏览器 UI(像原生 App) | App 型 PWA |
fullscreen | 全屏(无状态栏) | 游戏 |
推荐 standalone——用户感觉不到是在浏览器中运行。
4.3 图标要求
| 尺寸 | 用途 |
|---|---|
| 192x192 | Android 主屏幕图标 |
| 512x512 | Android 启动画面 |
| 180x180 | iOS 主屏幕图标(apple-touch-icon) |
| 任意 SVG | 现代浏览器矢量图标 |
5. Service Worker
5.1 Service Worker 的作用
Service Worker 是运行在浏览器后台的 JavaScript 线程——它拦截网络请求,实现离线缓存、后台同步、推送通知。
用户请求 → Service Worker 拦截
├── 缓存命中 → 直接返回缓存(离线可用)
├── 缓存未命中 + 在线 → 请求网络 → 缓存结果 → 返回
└── 缓存未命中 + 离线 → 返回离线页面5.2 缓存策略
| 策略 | 名称 | 行为 | 适用场景 |
|---|---|---|---|
| Cache First | 缓存优先 | 先查缓存,没有才网络 | 静态资源(JS/CSS/图片) |
| Network First | 网络优先 | 先网络,失败用缓存 | API 数据、页面 HTML |
| Stale While Revalidate | 先旧后新 | 返回缓存,后台更新 | 非关键但需要新鲜的数据 |
| Network Only | 仅网络 | 不缓存 | 实时数据(支付、认证) |
| Cache Only | 仅缓存 | 只读缓存 | 构建时预缓存的资源 |
5.3 Next.js 的 Service Worker 方案
| 方案 | 特点 | 推荐度 |
|---|---|---|
| @serwist/next | Serwist(next-pwa 继任者),Workbox 底层 | ⭐⭐⭐⭐⭐ |
| next-pwa | 经典方案,但维护放缓 | ⭐⭐⭐ |
| 自定义 Service Worker | 完全控制 | ⭐⭐⭐(需要手写) |
| Workbox | Google 出品,底层工具 | ⭐⭐⭐⭐ |
推荐 @serwist/next——它是 next-pwa 的现代继任者,与 App Router 兼容性最好。
5.4 预缓存 vs 运行时缓存
| 类型 | 时机 | 内容 | 优势 |
|---|---|---|---|
| 预缓存 | 安装 Service Worker 时 | JS/CSS/关键页面 | 离线立即可用 |
| 运行时缓存 | 用户访问时 | API 响应、图片 | 按需缓存,节省空间 |
6. 离线体验设计
6.1 离线策略
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 离线页面 | 显示友好的离线提示页面 | 所有 PWA 最低要求 |
| 离线阅读 | 缓存已访问的内容 | 新闻、博客、文档 |
| 离线操作 | 允许用户操作,上线后同步 | 表单、任务管理 |
| 完全离线 | 应用核心功能离线可用 | 工具类 App |
6.2 离线数据同步
用户离线操作(创建/修改/删除)
↓
操作存入 IndexedDB 队列
↓
网络恢复
↓
Background Sync API 触发
↓
逐个提交离线操作到服务器
↓
处理冲突(Last Write Wins / 合并)6.3 离线 UI 反馈
| 状态 | UI 反馈 |
|---|---|
| 在线 | 正常显示 |
| 离线 | 顶部 Banner 提示"当前离线" |
| 操作排队 | 显示"待同步"标记 |
| 同步中 | 显示同步进度 |
| 同步完成 | 清除标记 |
| 同步失败 | 显示错误 + 重试按钮 |
7. 推送通知
7.1 Web Push 架构
服务端发送推送请求
↓
Push Service(Google FCM / Apple APNs)
↓
用户设备接收
↓
Service Worker 触发 push 事件
↓
显示系统通知7.2 推送通知的最佳实践
| 实践 | 说明 |
|---|---|
| 延迟请求权限 | 不要一打开就弹权限请求 |
| 说明价值 | 告诉用户"订阅后会收到什么" |
| 控制频率 | 每天不超过 2-3 条 |
| 提供退出 | 应用内提供取消订阅的选项 |
| 相关性 | 只推送用户关心的内容 |
| 及时性 | 推送时效性强的信息 |
7.3 权限请求时机
| ❌ 糟糕的时机 | ✅ 好的时机 |
|---|---|
| 首次访问就弹窗 | 用户完成注册后 |
| 随机弹出 | 用户主动点击"接收通知" |
| 无上下文 | 在相关场景中(如"订阅订单更新") |
8. PWA 适用性评估
8.1 何时选择 PWA
✅ 适合 PWA 的场景:
- 内容消费型应用(新闻、博客、文档)
- 轻量工具型应用(待办、记账、翻译)
- 电商(浏览 + 收藏 + 推送)
- 需要跨平台但预算有限
❌ 不适合 PWA 的场景:
- 需要深度系统集成(相机高级功能、蓝牙)
- 高性能图形(3D 游戏)
- 必须在应用商店分发
- iOS 用户为主(PWA 限制较多)
8.2 PWA vs 原生 App vs 混合 App
| 维度 | PWA | React Native | Flutter | 原生 |
|---|---|---|---|---|
| 开发成本 | 最低 | 中 | 中 | 最高 |
| 性能 | 良好 | 好 | 优秀 | 最佳 |
| 离线能力 | 有限 | 完整 | 完整 | 完整 |
| 系统集成 | 有限 | 大部分 | 大部分 | 完整 |
| 更新速度 | 即时 | 即时/商店 | 即时/商店 | 商店审核 |
| SEO | ✅ | ❌ | ❌ | ❌ |
| 安装门槛 | 几乎无 | 需要下载 | 需要下载 | 需要下载 |
本章小结
- 移动优先:触摸交互 ≥ 44px 点击区域、安全区域适配、移动端性能预算更严格
- 响应式图片:Next.js
<Image>自动处理 srcset、格式转换、懒加载 - PWA 三要素:HTTPS + Web App Manifest + Service Worker
- 缓存策略:静态资源 Cache First,API 数据 Network First,非关键数据 Stale While Revalidate
- Service Worker:推荐 @serwist/next(next-pwa 继任者)
- 离线设计:操作存入 IndexedDB → 网络恢复后 Background Sync 同步
- 推送通知:延迟请求权限、说明价值、控制频率
- 选型:内容/工具类应用适合 PWA,需要深度系统集成的选择原生/RN/Flutter