要把 Web 应用上架到 App Store 和 Google Play,需要用 Capacitor 把构建好的 Web 应用封装进原生项目,添加一些原生功能,创建 Apple 和 Google 开发者账号,签名并构建 iOS 和 Android 二进制文件,让真实用户测试,准备好商店页面并提交审核。现有 HTML、CSS 和 JavaScript 会继续在原生 WebView 中运行,因此无需重写应用。
以下是 11 个步骤,按避免等待的顺序排列,并附上适用于 Capacitor 8 的命令,以及 2026 年 10 月适用的商店规则。
准备工作
- 可构建为静态文件的 Web 应用(React、Vue、Angular、Svelte、Next.js 静态导出、Nuxt generate、纯 HTML,或用 Lovable、Bolt 等 AI 构建器做的应用)。
- Node.js 22 或更高版本(Capacitor 8 的要求),以及 Bun 或其他包管理器。
- Android 构建需要 Android Studio。
- iOS 构建需要 macOS 上的 Xcode 26;没有 Mac 的话就用云构建服务。
- Apple 每年 99 美元,Google 一次性 25 美元。
合理的时间线
| 阶段 | 典型耗时 | 可以并行吗? |
|---|---|---|
| 开发者账号注册 | 数小时(个人)到 2 周(需新申请 D-U-N-S 编号的组织) | 是的,第一天就能开始 |
| Capacitor 配置与首次真机运行 | 1 天 | |
| 移动端打磨与原生功能 | 3 天到 3 周 | |
| 签名与首次构建 | 1 天 | |
| Google Play 封闭测试(新个人账号) | 至少 14 天,随后审核正式版发布权限 | 可以,使用 iOS TestFlight |
| 应用商店详情页 | 1 到 2 天 | 是 |
| App 审核 | Apple 通常需要 1 到 2 天,Google 则从几小时到几天不等 |
第一步:今天开始注册开发者账号
账号验证是没法加速的一步,所以在开始写代码前先把它做完。
- Apple 开发者计划: 每年 99 USD。个人需要开启双重认证,且 Apple Account 使用法定姓名。组织还需要 D-U-N-S 编号、公司网站以及该域名下的工作邮箱。
- Google Play 控制台: 一次性支付 25 USD。组织用户需要 D-U-N-S 编号。会要求进行身份验证。
如果你以个人身份加入 Google Play,你的账号将受限于封闭测试规则(步骤 9)。完整流程如下: 如何创建 Apple 和 Google Play 开发者账号.
第二步:让 Web App 适配移动端
Capacitor 在 WebView 中运行你的应用。在浏览器标签页里没问题的事情,放到 App 里就会显得不对劲:
- 静态构建输出。 Capacitor 从 App bundle 加载文件。服务端渲染不会在手机上运行。请使用你框架的静态输出(
next build配合output: 'export',nuxt generate、Vitebuild)。API 的调用仍会像以前一样通过 HTTPS 发到你的后端。 - 在 file origin 下也能正常工作的客户端路由。 请使用 history 路由(Capacitor 会从
https://localhost在 Android 上,capacitor://localhost在 iOS 上提供应用,因此可以正常工作),并确保深层 URL 能回退到index.html. - CORS。 把 Capacitor 的 origin 加入 API 的允许 origin 列表:
capacitor://localhost(iOS) 和https://localhost(Android)。 - 触摸,而非悬停。 移除仅靠悬停的菜单。点击目标至少为 44 x 44 pt。
- 安全区域。 添加
viewport-fit=cover添加到 viewport meta 标签,并为页眉和底部栏留出内边距env(safe-area-inset-top)以及env(safe-area-inset-bottom). - 离线与弱网环境。 显示合适的状态,而不是白屏。审核人员会在不稳定的 Wi-Fi 下测试。
- 不要出现“下载我们的 App”横幅 也不要有把用户引到你的网站完成核心任务的链接。
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
第三步:添加 Capacitor
在你的 Web 项目根目录中:
bun add @capacitor/core
bun add -d @capacitor/cli
bunx cap init "My App" com.example.myapp --web-dir dist
bun add @capacitor/ios @capacitor/android
bun run build
bunx cap add ios
bunx cap add android
使用实际的输出文件夹 --web-dir: dist 针对 Vite, out 用于 Next.js 静态导出, .output/public 用于 Nuxt generate, build 用于 Create React App, dist/<project>/browser 用于 Angular。
Bundle ID(com.example.myapp) 一旦应用上架商店,此设置即为永久。请使用您控制的反向域名。
你的 capacitor.config.ts 现在看起来像这样:
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'My App',
webDir: 'dist',
};
export default config;
Capacitor 8 默认使用 Swift Package Manager 创建 iOS 项目,因此无需 CocoaPods。每次更改 Web code 时:
bun run build
bunx cap sync
运行:
bunx cap run ios
bunx cap run android
提交 ios/ 以及 android/ 文件夹。这些是您将编辑的源 code(权限、图标、签名)。更多背景信息,请参阅 使用 Capacitor 将 Web 应用转换为移动应用有多简单,如果您使用 AI 工具构建,我们的 Lovable 转移动端指南中描述的设置相同.
第 4 步:添加证明应用价值的原生功能
Apple 审核指南 4.2(最低功能)会拒绝只是把网站套个壳的 App。你不需要几十个原生功能,但 App 得像个 App。常见且实用的补充功能:
| 功能 | 插件 |
|---|---|
| 推送通知 | @capacitor/push-notifications |
| 原生 Google、Apple 和 Facebook 登录 | @capgo/capacitor-social-login |
| Face ID / 指纹解锁 | @capgo/capacitor-native-biometric |
| 相机与相册 | @capacitor/camera |
| 应用内购买与订阅 | @capgo/capacitor-native-purchases |
| 原生分享面板 | @capacitor/share |
| 触感反馈 | @capacitor/haptics |
| 状态栏与启动页 | @capacitor/status-bar, @capacitor/splash-screen |
| 应用评分提示 | @capgo/capacitor-in-app-review |
| Live Updates | @capgo/capacitor-updater |
示例:显示原生分享面板,而非“复制链接”按钮。
import { Share } from '@capacitor/share';
import { Capacitor } from '@capacitor/core';
export async function shareLink(url: string, title: string) {
if (Capacitor.isNativePlatform()) {
await Share.share({ title, url });
} else {
await navigator.clipboard.writeText(url);
}
}
如果你销售数字内容或订阅,现在就要规划支付。Apple 和 Google 在大多数情况下要求数字商品使用其应用内购买系统。美国 App Store 的应用在 2025 年法院裁决后也可以链接到外部网页结账;其他地区有各自的规则。实物商品和服务(配送、打车)可以使用 Stripe 或任何支付服务商。详见 使用 Capacitor 实现应用内购买.
完整的 Capgo 插件目录位于 插件页面.
第 5 步:图标、启动页与权限说明
仅需两张源图即可生成所有尺寸的图标和启动页:
bun add -d @capacitor/assets
# assets/icon.png 1024x1024, assets/splash.png 2732x2732
bunx capacitor-assets generate
在 iOS 上,你使用的每项权限都必须在 ios/App/App/Info.plist中提供用途说明,否则在请求权限时应用会崩溃,且会被 Apple 拒绝:
<key>NSCameraUsageDescription</key>
<string>Take a photo of your receipt to attach it to an expense.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose receipt photos from your library.</string>
请写明具体理由。“此应用需要相机”会被拒绝。
在 Android 上,插件会自动把大多数权限添加到 manifest。请移除未使用的权限;Google 会要求你说明敏感权限的理由。
另外,请添加 隐私清单 (PrivacyInfo.xcprivacy)如果你的 code 使用了 required-reason API。请参阅 iOS 应用隐私清单指南.
步骤 6:配置签名
iOS。 你需要一个 Apple Distribution 证书(含私钥,通常导出为 .p12)以及对应 bundle ID 的 App Store Connect provisioning profile。Xcode 可以自动创建,你也可以自己创建,甚至无需 Mac。阅读 iOS 证书与 provisioning profile 详解; iOS 证书生成器 会在浏览器中处理 CSR 步骤。
Android。 创建一个 upload keystore 并做好备份。Google Play 使用 Play App Signing,最终的应用签名密钥由 Google 保管,你用自己的密钥为 upload 签名。
keytool -genkeypair -v -keystore upload-keystore.jks -alias upload \
-keyalg RSA -keysize 2048 -validity 10000
或者使用 Android keystore 生成器.
第 7 步:构建发布版二进制文件
本地
iOS (需在装有 Xcode 26 的 Mac 上操作,自 2026 年 4 月起 App Store 上传必须使用该版本):
bunx cap open ios- 选择 应用 选择 target,设置版本号和构建号,并在 Signing & Capabilities 中选择你的 Team。
- 选择 任何 iOS 设备 (arm64),然后 产品 > 归档.
- 在 Organizer 中, Distribute App > App Store Connect > Upload.
Android:
bun run build
bunx cap sync android
cd android
./gradlew bundleRelease
AAB 文件位于 android/app/build/outputs/bundle/release/。配置 signingConfigs 中的 android/app/build.gradle 使用你的 keystore,并从环境变量中加载密码。
请确保 targetSdkVersion 是 36。自 2026 年 8 月 31 日起,Google Play 要求新应用和更新以 Android 16(API 36)为目标,这也是 Capacitor 8 的默认值。
在云端
如果你没有 Mac,或者希望构建能在 CI 中复现, Capgo 构建 在托管机器上构建两个平台的版本,并可直接上传到 TestFlight 和 Google Play:
bunx @capgo/cli@latest build credentials save --appId com.example.myapp --platform ios
bunx @capgo/cli@latest build credentials save --appId com.example.myapp --platform android
bunx @capgo/cli@latest build request com.example.myapp --platform ios --path .
bunx @capgo/cli@latest build request com.example.myapp --platform android --path .
凭据仅用于构建,不会存储在 Capgo 服务器上。请参阅 Capgo 构建文档.
第 8 步:分发给测试人员
在 Apple 或 Google 审核之前,先把构建装到真机上。
- iOS:上传到 TestFlight。内部测试人员(最多 100 名团队成员)在构建处理完成后即可获取;外部测试人员(最多 10,000 名)则需经过简短的 Beta App Review。
- Android:在 Play Console 中创建内部测试版本并分享加入链接。
测试注册与登录、沙盒环境下的支付、推送通知、离线行为、键盘遮挡输入框的情况,以及 Android 上的返回键行为。各选项详情如下: 如何向测试人员分发 iOS 和 Android 应用.
第 9 步:运行 Google Play 封闭测试(新个人账号)
如果您的 Play 账号是 2023 年 11 月 13 日之后创建的个人账号,那么必须先进行一次 至少有 12 名测试人员报名并连续参与 14 天的封闭测试。之后在 Play Console dashboard 中申请正式版权限,Google 通常会在两周内完成审核。
一旦有了可用的 Android build,就尽快开始。用 Google Group 作为测试人员名单,方便用户自行加入。在这 14 天里持续发布更新并收集反馈,因为 Google 会问您学到了什么。组织账号不受此限制。
第 10 步:准备商店详情并提交
两个商店都需要:应用名称、描述、符合尺寸要求的截图、图标、隐私政策 URL、隐私披露(Apple App Privacy 和 Google Data safety)、年龄分级问卷、支持联系方式,如果应用有登录功能还需提供演示账号。Google 还需要一张 1024 x 500 的特色图片。规格与限制: 如何准备 App Store 和 Google Play 的商店详情.
在 iOS 上提交: 在 App Store Connect 中创建版本,选择 TestFlight build,填写 App Review Information(演示账号、备注),然后点击 添加以供审核,然后 提交.
在 Android 上提交: 使用 AAB 创建正式版(或从测试轨道提升已测试的版本),完成所有 应用内容 声明,设置国家/地区,然后提交审核。
Web 应用最常遇到的拒绝原因:
- 4.2 最低功能要求:应用只是你的网站。请增加原生价值,并移除类似浏览器的 UI。
- 需要登录但未提供演示账号,或者账号无法使用。
- 缺乏注重隐私的登录选项 在 iOS 上提供 Google 或 Facebook 登录时,Guideline 4.8 要求提供限制数据收集的等效选项;通常选择 Sign in with Apple。
- 未提供注销账号功能 在应用内,当用户能够创建账户时。
- 通过 Stripe 进行数字购买 需要使用应用内购买的场景。
- iPad 布局错乱 因为项目默认支持 iPad。
- 权限描述缺失或模糊。
我们的 首次 App 审核指南 以及 iOS 应用提交指南 逐一详细说明。
第 11 步:无需等待审核即可发布更新
上线后,大部分改动仍会落在 Web code 中。与其每次修复都做一次新的商店构建,不如添加 Live Updates。Capgo 更新程序会从 Capgo 下载新的 Web bundle,并在下次启动时应用;如果新 bundle 启动失败,还会自动回滚。
bun add @capgo/capacitor-updater
bunx cap sync
bunx @capgo/cli@latest init
调用 notifyAppReady() 应用启动后,更新程序就能确认该 bundle 运行正常:
import { CapacitorUpdater } from '@capgo/capacitor-updater';
CapacitorUpdater.notifyAppReady();
之后,每次发布 Web code 都只需一条命令:
bun run build
bunx @capgo/cli@latest bundle upload --channel production
只要更新不改变应用的主要用途或绕过支付规则,两大应用商店都允许这种解释型 code。原生更改(新插件、新权限、Capacitor 升级)仍需走应用商店。了解更多请阅读 Capgo 实时更新 以及 updater 文档.
在首次向应用商店提交前添加该插件。它必须包含在用户安装的二进制包中,否则你的第一个 live update 要等到第二次商店发布后才能触达用户。
最后,实现自动化:通过 GitHub Actions 或 GitLab CI 在每次打 tag 时 build 并 upload,每次 merge 时推送 Live Updates 到 main,并将原生版本发布留给原生更改。
上线前检查清单
- Apple 和 Google 账号已通过审核,协议已接受
- App 支持离线运行或有明确的离线状态提示
- 已处理安全区域、键盘及 Android 返回键
- 具备至少几项 Web 端无法提供的原生功能
- 图标、启动页、权限文案、隐私清单
- 分发证书、App Store 描述文件及上传用 keystore 已备份
- Android 目标 SDK 36,iOS 使用 Xcode 26 构建
- 已在真机上完成 TestFlight 和 Play 内部测试
- 已完成 Google Play 封闭测试(个人账号)
- 两个商店的详情页均已完成,并添加了演示账号
- 若支持用户注册,需提供应用内注销账号功能
- 首个二进制包中已包含 Live Updates 插件
故障排除
启动时出现白屏。 webDir 指向了错误的文件夹,或者你忘了 bunx cap sync 构建后。请检查 ios/App/App/public 以及 android/app/src/main/assets/public 包含 index.html.
API 调用仅在 App 内失败。 CORS:允许 capacitor://localhost 以及 https://localhost。同时检查你没有调用 http:// URL,iOS 默认会拦截这些 URL。
刷新或 deep link 后路由 404。 使用可回退的路由器 index.html,或对旧版配置使用 hash 路由。
Xcode 签名错误。 请查看错误列表: iOS 证书与 provisioning profile 详解.
升级后 Gradle 构建失败。 Capacitor 8 需要 Android Studio Otter (2025.2.1) 或更高版本以及 JDK 21,详情请参阅 Capacitor 8 升级指南。其他 Gradle 错误请参阅 如何解决 Capacitor 中的 Android 构建错误.
目标 SDK 的 Google Play 上传被拒绝。 设置 targetSdkVersion = 36 中的 android/variables.gradle.