“说到跨平台开发,很多人脑子里蹦出来的第一反应是 Flutter 和 React Native。但我在实际项目里折腾了一圈之后,发现还有一类项目最适合用一套 Web 技术栈直接包一层原生外壳——这个角色的主流选择,目前就是 Capacitor。Capacitor 不是一个 UI 框架,它更像一套连接 Web 应用与原生系统的管道,让用 Vue、React 或者原生 JS 写好的页面,直接跑进 iOS 和 Android 的 WebView 里,同时又能调用摄像头、定位、震动这些原生能力。这篇文章不是官方文档的翻译,而是基于我实际跑通几个项目后的整理,涉及环境搭建、日常开发流、插件接入,还有大家搜得最多的‘capacitor 打包工具’到底怎么用。”
1. Capacitor解决了什么问题:跨平台技术谱系里的特殊位置
1.1 用Web技术写原生壳,它是一个“中间态”
做跨平台开发这些年,方案换了一茬又一茬。从早期的 Hybrid、H5 套壳,到后来的 React Native、Flutter,再到现在的 Capacitor,每一代工具其实都在解决同一个问题:怎么让一套业务代码尽量多端复用,同时又不至于把用户体验做成一个纯网页。
Capacitor 的定位很有意思——它不高性能渲染,不搞自绘引擎,而是老老实实把你写好的 Web 静态资源丢到一个原生 WebView 里。Android 上默认跑的是 Android System WebView,iOS 上默认跑的是 WKWebView。听起来好像很“复古”,但现代 WebView 的渲染能力早已不是当年那副样子,尤其是 WKWebView 在 JavaScript 引擎和页面渲染上的表现,应付绝大多数业务界面完全够用。
真正让 Capacitor 区别于“套壳”的,是它自己搭的那条原生桥。你的 Web 代码通过 JavaScript 调用一个个封装好的插件,插件在原生层用 Java/Kotlin 或 Swift/Objective-C 去调系统 API,再把结果异步回传给 Web 层。这个通信过程是标准化的,不在 Web 层暴露乱七八糟的原生对象,也不用像老式混合开发那样靠 URL 拦截做交互。它更像一个中间态:开发模型仍然是 Web,能力边界却直通原生。
1.2 从Cordova到Capacitor:牌匾换了,思路也换了
很多老开发者对 Capacitor 的第一反应是“这不就是 Cordova 换了个名字吗”。确实,Capacitor 由 Ionic 团队开发,思想上延续了 Cordova 那套 Web+原生壳的路线,但底子和使用体验都已经换了代。
最大的变化是项目管理方式。Cordova 时代,插件和平台都是有自己一套 CLI 去管理,原生工程反而不像是“一等公民”。而 Capacitor 反过来:npx cap add android之后,android/目录就是一个完完整整的 Gradle 工程,ios/就是一个标准的 Xcode 工程。你可以随时打开 Android Studio 或 Xcode 去改原生代码,原生部分不再是一个只能看不能碰的黑盒。这一点在团队协作时特别重要,原生同事和前端同事可以在同一个项目里各司其职。
其次,Capacitor 的插件机制更“原生”。Cordova 插件必须遵循它那套 JS 桥接规范,很多插件为了兼容老设备写了一堆兼容代码。而 Capacitor 官方推荐直接用 Swift 和 Java/Kotlin 写原生插件,JavaScript 侧只做声明和调用。写完之后,CLI 会自动生成对应的桥接表,不需要手动注册,自定义能力门槛低了不少。
还有一点容易被忽略:安全模型。Capacitor 默认只允许 WebView 加载本地资源和你明确配置的服务器地址,导航、外链跳转、混合内容都有更严格的限制。这虽然是给开发添了一点配置成本,但从 App 的健壮性来看,比 Cordova 那种“默认啥都放行”的态度让人放心得多。
1.3 哪些项目适合选Capacitor
我个人的判断标准,大致有这么几条。
如果你的团队以 Web 前端为主,不太想养原生双端人员,业务以信息展示、表单流程、数据管理为主,Capacitor 非常舒服。一套 Vue/React 代码,构建产物丢给它,剩下就是处理插件和打包。
如果项目已经有一套成熟的 Web 应用或 PWA,想快速进入应用商店,Capacitor 几乎是成本最低的路径。你不需要重写 UI,不需要学习新的 UI 组件模型,只要把现有构建产物接进去,再补上原生权限和打包配置就行。
反过来,如果产品核心是大量 Canvas 动画、实时音视频编解码、复杂手势交互,或者对启动速度和内存占用极度敏感,那 Capacitor 不是最优解。Flutter 或者纯原生会是更好的选择。它不是万金油,但它是很多业务场景下“性价比最高”的那条路。
还有一个常见误区:把 Capacitor 拿来当性能瓶颈的替罪羊。做个普通表单应用,卡不卡主要看你的 Web 代码本身,而不是 WebView。真开到性能瓶颈的项目,多半是压根不该选 Web 技术栈。
2. 动手初始化一个Capacitor项目:环境与目录结构逐个理清
2.1 需要的工具链和版本要求
Capacitor 的环境依赖说简单也简单,说复杂也复杂。它本身是个 Node 工具链,所以第一件事是装一个合适的 Node 版本。以最新的 Capacitor 7 为例,Node 至少需要 20 以上;还在用 Capacitor 6 的话,Node 18 也够用。我建议直接用 Node LTS,别在这上面省事。
原生端工具是真正的门槛。Android 必须装 Android Studio,并且把 SDK、命令行工具、平台工具都配好。Linux 或 Windows 上还得留意ANDROID_HOME环境变量,很多新手的第一个报错就是SDK location not found,其实就是这个变量没设置或者指向不对。iOS 就简单粗暴了,必须有一台 macOS,装上 Xcode,再把 Command Line Tools 装齐。Windows 上只能做 Android,这是硬限制,绕不过去。
Java 环境也容易出问题。现代 Capacitor 7 要求 JDK 21,Capacitor 6 配 JDK 17 比较稳。注意你打开 Android Studio 自带的是它自己的 JBR,但命令行执行gradlew时用的可能又是系统JAVA_HOME。我见过好几回在 IDE 里能构建,命令行一间就Unsupported class file major version,基本都是 JDK 版本不一致闹的。动手前先在终端跑一遍node -v、java -version,能省很多事。
2.2 从零初始化项目并加入原生平台
最省事的做法,是用官方脚手架起一个带 Capacitor 配置的模板项目:
npm init @capacitor/app@latest它会问你应用名称和包 ID,然后生成一个带基础页面、已经装好 Capacitor 依赖的项目。不过大多数老实干项目的人不会这么干,更多人跟我一样:先有一个 Web 项目,再把它“Capacitor 化”。
以 Vite + Vue 项目为例,操作流程是这样的。先在项目里装核心依赖:
npm install @capacitor/core @capacitor/cli然后初始化配置:
npx cap init "我的应用" com.example.myapp --web-dir=dist这里--web-dir=dist告诉 Capacitor:你打包后的前端静态文件放在dist/。这个目录名得跟你的实际构建输出目录完全一致,写错了后面同步会告诉你找不到公共文件。
接着加入平台依赖:
npm install @capacitor/android @capacitor/ios npx cap add android npx cap add ios执行完,项目根目录下会出现android/和ios/两个原生工程。注意一点:npx cap add会尝试读取你当前的构建产物,如果dist/还不存在,它会创建原生目录但提示找不到 web assets。所以我通常先把前端npm run build一遍,再执行 add。省得后面copy时报错。
2.3 capacitor.config.ts里每个常用配置在管什么事
项目根目录会生成capacitor.config.ts,这是整个 Capacitor 的中枢配置。我贴一份比较典型的示例,配合注释看:
import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.myapp', appName: '我的应用', webDir: 'dist', android: { allowMixedContent: false }, ios: { contentInset: 'always' }, server: { androidScheme: 'https', cleartext: false } }; export default config;appId是反向域名格式的包标识符,Android 上它会成为应用的 applicationId,iOS 上对应 Bundle Identifier。这个值一旦上架发布,后面很难再改,相当于 App 的身份证号,定配置值时务必慎重。
webDir指向的是构建产物目录,静态文件会被原封不动拷贝进原生工程。server.androidScheme默认是https,这其实是 Capacitor 一个很聪明的设计——不要在本地页面里出现http://localhost这种 scheme,否则很多 Web API 和混合内容策略都会出问题。如果你在上传下载或者图片加载时遇到奇怪的网络限制,先怀疑这里。
cap config可以查看最终生效的配置,npx cap doctor可以检查当前环境是否有缺失的依赖。这两个命令我几乎隔几天就用一次,排查问题比翻文档快得多。
3. 日常开发核心工作流:改代码、同步、上真机
3.1 开发阶段先用浏览器验证,别急着跑原生
Capacitor 项目里,绝大多数业务逻辑依然跑在 Web 环境里。所以我的开发习惯是:前端写好功能,先在浏览器里过一遍,保证逻辑和样式没问题,再往原生环境走。
这一步没什么特殊操作,就是正常的npm run dev,然后打开你平时的浏览器。唯一要注意的是:你引用的 Capacitor 插件在浏览器环境里不一定全部可用。像Preferences、Device这类有 Web 降级实现的,浏览器里能跑。但Camera、Geolocation这些依赖于原生能力或浏览器自身接口的插件,可能会直接抛unavailable。遇到这种情况别慌,写代码时自己做一个环境判断,或者做好 try/catch,不要让它从浏览器阶段就一直飘红。
用浏览器开发的好处是迭代快,样式热更新秒级生效。等到功能攒到一定量,再同步到原生环境,跑一遍真实场景。
3.2 cap sync到底同步了什么,和cap copy有什么区别
这俩命令看着像,实际干的活不一样。npx cap copy只做一件事:把前端的构建产物和插件相关资源,拷贝到android/和ios/的原生工程里。它不碰依赖,也不重新编译任何原生代码。适合日常改完前端代码后快速刷新资源。
npx cap sync是一个组合动作:先跑 copy,再做原生依赖同步。具体来说,它会根据package.json里的 Capacitor 插件列表,重新生成或更新原生工程中的插件引用;iOS 端还会自动执行pod install。所以当你新增或删除插件、修改了capacitor.config.ts、或者升级了某个@capacitor/*包版本时,都必须用sync,光 copy 是起不了作用的。
我建议的日常节奏是:改前端代码,执行前端 build,然后npx cap copy android;装了新插件或者改了原生工程相关配置,再老老实实跑npx cap sync。有人喜欢每次都 sync,也不是不行,就是 iOS 那边每次都要跑 CocoaPods,耗时比较肉疼。
3.3 真机调试与热更新的现实状态
先说 Android。插上开好开发者模式的设备,执行npx cap run android,Capacitor CLI 会先打包再安装到设备上。如果设备已经通过 adb 连接,--device参数能让你指定目标设备。运行起来之后,Chrome 浏览器打开chrome://inspect,就能看到 WebView 的调试面板,DOM、Console、Network 全都有,和调试普通网页几乎没差别。这是我觉得 Capacitor 调试体验最舒服的地方——底子和 Web 一样,工具链全是现成的。
iOS 的调试链路类似,连接真机后在 Xcode 里 Run,然后打开 Safari 的“开发”菜单,就能选中设备的 WebView 进行调试。代价是必须有一个有效的开发者证书,否则跑不到真机上。
关于热更新,Capacitor 本身不提供像 Flutter hot reload 那种体验。它有一个 Live Reload 功能,原理是通过server.url配置指向你本地或局域网内的开发服务器,让 WebView 直接加载开发环境页面,改代码后自动刷新,效果确实很接近热更新。但这里我必须强调一个坑:发布之前一定要把server.url从配置里删掉,否则用户打开你的 App,实际加载的是网络地址,而不是你打包进去的本地资源。这个错误在上架审核时属于严重的架构问题,轻则被打回,重则惹来安全质疑。
我自己的做法是准备两个配置文件,开发时用一个带server.url的版本,构建发布时再用一个干净的版本。靠手动改很容易漏,靠脚本切换要稳得多。
4. 插件体系与原生能力接入:从常用插件到原理认知
4.1 内置插件地图:Camera/Geolocation/Preferences这些到底怎么用
Capacitor 官方维护了一批基础插件,覆盖了绝大多数 App 的常见原生需求。我把经常用到的几个列个表,方便你建立整体印象:
| 插件 | 能力 | 典型场景 |
|---|---|---|
| @capacitor/camera | 拍照、选图、裁剪 | 头像上传、证件扫描 |
| @capacitor/geolocation | 定位 | 打卡、地图展示 |
| @capacitor/preferences | 轻量键值存储 | 登录态、用户设置 |
| @capacitor/device | 设备信息 | 机型适配、埋点统计 |
| @capacitor/network | 网络状态监听 | 断网提示、弱网处理 |
| @capacitor/splash-screen | 启动画面控制 | 冷启动过渡 |
| @capacitor/status-bar | 状态栏样式 | 沉浸式页面 |
| @capacitor/share | 系统分享 | 内容转发 |
| @capacitor/haptics | 震动反馈 | 点击反馈、下拉刷新 |
用法都差不多,装包之后直接 import 调用。比如拍照:
import { Camera, CameraResultType } from '@capacitor/camera'; const image = await Camera.getPhoto({ quality: 90, allowEditing: true, resultType: CameraResultType.Uri, source: CameraSource.Camera });看到没有,调用方式和普通的异步函数没有区别,返回的image对象里包含webPath、文件 uri 等信息。你不需要关心 Android 上是 Intent 还是别的实现,插件层已经把差异抹平了。
Preferences在很多场景下可以替代 localStorage,因为它会真正写入原生应用的存储空间,逻辑上更符合原生应用的预期:
import { Preferences } from '@capacitor/preferences'; await Preferences.set({ key: 'token', value: 'abc123' }); const { value } = await Preferences.get({ key: 'token' });这些东西官网文档写得很清楚,更值得留意的是它们各自的平台差异和权限要求。
4.2 原生权限配置:Manifest和Plist是绕不过去的
这是新手翻车率最高的地方。Capacitor 的插件调用原生 API 时,原生系统本身是有权限检查的。你用Camera.getPhoto(),Android 侧需要在AndroidManifest.xml里声明相机权限;iOS 侧则必须在Info.plist里提供用途描述字符串。
Android 的AndroidManifest.xml在android/app/src/main/下。比如要用相机和定位,就得加:
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />注意:很多插件封装了运行时权限请求,但这不代表你可以跳过 Manifest 声明。声明是安装时告知系统,运行时请求是 App 内部弹窗,两者缺一不可。漏了 Manifest 声明,常常不是直接报权限拒绝,而是插件内部抛一个非常迷惑的异常,排查半天到不了点上。
iOS 的 Info.plist 就更“娇气”了。没有添加对应的用途描述,调用相应 API 时 App 会直接崩溃,而且崩溃日志里只会给你一句“This app has crashed because it attempted to access privacy-sensitive data without a usage description”,非常吓人。常见要加的有:
<key>NSCameraUsageDescription</key> <string>需要使用相机拍摄照片</string> <key>NSPhotoLibraryUsageDescription</key> <string>需要读取相册以选择图片</string> <key>NSLocationWhenInUseUsageDescription</key> <string>需要获取位置信息以提供服务</string>文字写清楚你为什么要这个权限,审核时会有人看,乱写也会被驳回。这块建议一开始就把所有可能用到的权限提前布好,别等测试时再加,来回打包很耗时间。
4.3 自定义插件和第三方插件生态
官方插件覆盖面虽然广,但总会遇到一些边界需求,比如 NFC、指纹支付、特殊蓝牙设备。这种时候有两条路。
第一条路是找社区插件。@capacitor-community组织下有很多第三方插件,质量参差不齐,有的非常活跃,有的两三年不更新。用之前我建议先看仓库的 Issues 和最近 Release 时间,如果主版本还停在 Capacitor 5 时代,那在当前项目里大概率编译不过去。
第二条路是自己写插件。Capacitor 自定义插件的基础结构比 Cordova 时期清爽得多。你创建 Packages 里的自定义 plugin,TypeScript 侧写接口声明,Android 侧用@CapacitorPlugin注解加一个类,iOS 侧用CAPPlugin子类实现同名方法。一个最简的 Echo 插件,原生方法就是拿到参数原样返回,用来验证桥路通不通,十几分钟就能跑通。
自己写的插件建议封装到独立 workspace 包里管理,别堆在主应用里。一是主应用原生代码多了以后构建变慢,二是插件本质上是可复用单元,拆出去以后多项目受益。我现在的团队就维护着几个内部插件,给多套 App 共用,收益很高。
5. 打包才是关键路径:Capacitor打包工具的完整流程拆解
5.1 “打包工具”的真相:CLI编排,不是一键图形化
网上搜“capacitor 打包工具”,会看到很多人把它当成一个像 HBuilderX 那样的可视化打包器。但实际用过就知道,Capacitor 并没有提供独立的一键打包界面。它的打包本质是一条完整的编译链条:
你的前端代码被构建成静态文件,npx cap sync把这些文件和原生工程信息一起整合进android/、ios/目录。之后的动作就不再是 Capacitor 的事了:Android 走 Gradle,iOS 走 Xcode。Capacitor 的 CLI 充其量是个“编排者”,真正出包的是原生构建链。
这个认知非常重要,因为它直接决定了排错思路。如果你发现包打不出来,或者打出来运行不对,先想清楚是哪一层出问题:是 Web 静态文件没同步进去,还是原生构建依赖缺失,还是签名配置错误。很多人一上来就重新初始化项目,其实都是没有把问题分层。
5.2 Android端打包发布全流程
Android 打包分 debug 和 release 两条路。调试时直接npx cap run android,或者用 Android Studio 点 Run,产物是app-debug.apk,自带 debug 签名,装到设备上就行。
正式发布要走 release。第一步,确认前端构建产物是最新的,然后同步一次:
npm run build npx cap sync android然后检查android/app/build.gradle里的签名配置。默认的 release 构建没有配置签名,直接打出来的包是未签名的,不能安装,更不能上架。需要先准备一个 keystore:
keytool -genkey -v -keystore release.jks -keyalg RSA -keysize 2048 -validity 10000 -alias androidkey生成后在build.gradle中加入签名信息:
android { signingConfigs { release { storeFile file('/你的路径/release.jks') storePassword System.getenv("KEYSTORE_STORE_PASSWORD") keyAlias System.getenv("KEYSTORE_KEY_ALIAS") keyPassword System.getenv("KEYSTORE_KEY_PASSWORD") } } buildTypes { release { signingConfig signingConfigs.release minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } }我把口令都改成了环境变量读取,避免有人手滑把密钥提交进 git 仓库。测试期图省事硬编码的,上线前全部清理掉,这个风险值得花十分钟防住。
接下来在android/目录执行:
./gradlew assembleRelease生成 APK 在android/app/build/outputs/apk/release/app-release.apk。如果目标是上架 Google Play,那要的是 AAB 格式:
./gradlew bundleRelease产物在android/app/build/outputs/bundle/release/app-release.aab。两者用的是同一个签名配置,区别只是分发渠道不同。
5.3 iOS端打包发布全流程
iOS 打包必须有 Xcode 和开发者账号。流程前一半和 Android 类似:前端构建、npx cap sync ios。然后执行npx cap open ios打开 Xcode 工程。
打开之后要做的第一件事是确认工程的 Bundle Identifier 和capacitor.config.ts里的appId一致,不一致的话真机安装和上传都会当场报错。接着在 TARGETS 的 Signing & Capabilities 里选择你的开发者 Team,让 Xcode 自动管理证书和描述文件。如果这一步是空的,说明 Xcode 还没配置账号,Preferences 里登录之后回来刷新。
正式出包走 Product -> Archive。Archive 完成后打开 Organizer,选择 Distribute App。如果只是内部测试,选 Development 或 Ad Hoc,导出 ipa 直接分发给设备;如果要上传 TestFlight 或 App Store,选 App Store Connect,Xcode 会自动完成签名、上传。
初次配置 iOS 工程经常在依赖同步上卡住。npx cap sync ios会自动执行pod install,但如果你的机器是第一次装 CocoaPods,或者公司网络访问 CocoaPods 仓库慢,那个等待时间真的很劝退。我的经验是提前把 CocoaPods 源配好镜像,并且养成装新插件后不急着同步、确认版本兼容再同步的习惯。
5.4 打包完经常遇到的几个“最后一公里”问题
打包本身跑通,不代表装上就能用。我总结几个实际项目中反复出现的问题。
第一个是 SPA 路由问题。如果你的 Web 端用的是 History 路由,比如 Vue Router 的createWebHistory,那么在本地静态资源环境下,直接访问一个深层路径时,WebView 找不到对应的物理文件。绝大多数网页首屏是从根路径进来的,所以问题常被掩盖;但只要你做了外部跳转回 App、push 通知点击等深链场景,白屏的体验就冒出来了。建议是监听appUrlOpen事件,在事件回调里处理深层路由跳转,不要依赖原生的整页加载。
第二个是混合内容拦截。Capacitor 的本地页面默认走httpsscheme,如果需要加载http://的图片或接口,会被 WebView 拦下来。开发调试时可以临时打开cleartext,上线前一定要确认线上服务全是 HTTPS。如果是接口域名比较复杂,优先用官方@capacitor/http插件走原生请求,从根上绕开 WebView 的网络限制。
第三个是混淆后插件失效。Android 开启minifyEnabled之后,如果 proguard 规则没配完整,某些插件类会被误删,运行时直接ClassNotFoundException。Capacitor 官方其实提供了自动引入的混淆规则,一般问题不大,但自定义插件和反射调用的部分要格外小心。首次开启混淆后,把所有插件功能完整回归一遍是必须的。
6. 实践总结与避坑纪要
6.1 版本匹配:跨版本项目最容易翻车的地方
Capacitor 生态最大的隐性成本不是学习,而是版本对齐。@capacitor/core、@capacitor/cli、各个官方插件的大版本号必须严格一致。假如核心包是 7.x,却装了一个 6.x 的@capacitor/camera,运行环境可能不会在编译期报错,而是到调用时给你一个莫名其妙的 undefined 或者方法不存在,非常难定位。
Loader 版本和 Node 环境的匹配也值得一提。Capacitor 6 我在 Node 16 上跑,CLI 各种提示版本不支持;升到 Node 20 之后,很多偶发问题直接消失。有时候问题不是代码,不是配置,就是工具链版本太旧。
升级 Capacitor 主版本的时候,别指望直接改package.json就完事。原生工程里的 Gradle 插件、Kotlin 版本、CocoaPods 依赖,都要跟着官方迁移指南调整。最稳的方式是用一个干净的测试项目,对比新旧版本生成的模板差异,再对照自己的项目手动修改。这个流程比较繁琐,但能省掉之后一星期的排错时间。
6.2 我在几个真实项目里踩过的具体坑
先讲图标和启动页。很多初学者不处理图标资源,结果装到手机上,桌面图标是 Capacitor 默认的 logo,启动画面也是默认的折叠 logo,看起来非常不专业。处理其实不复杂:装一个@capacitor/assets工具,准备一张icon.png和splash.png,然后执行:
npm install -D @capacitor/assets npx capacitor-assets generate --android --ios工具会自动生成各尺寸资源并覆盖到原生工程里。图标原图建议至少 1024x1024,启动图注意安全区域,别把核心内容画在会被状态栏遮住的位置。
再讲 WebView 缓存。发布新版本之后,用户端可能一直看到旧页面。我在一个上线项目里被这个问题折磨过两轮,最后解决方案是前端构建时给静态资源带 hash 文件名,同时给index.html设置no-cache响应头。Vite 默认会给 JS/CSS 加 hash,问题基本集中在这两层:资源路径带 hash 保证内容更新,入口 no-cache 保证每次请求都拿到最新入口。如果业务场景偏传统,后端不好配响应头,也可以用原生侧在启动时清理 WebView 缓存兜底,但这是下策,能配置服务端就优先配服务端。
最后是package.json里 Capacitor 插件加了,但sync之后原生工程里没生效的情况。早期版本偶尔会有原生依赖缓存,解决办法是先npx cap sync --clean,然后再构建。如果还不行,检查是不是用了 containerd 之类的 Docker 构建环境导致文件写入权限异常。本地开发一切正常,只有 CI 机器上出问题,优先怀疑构建环境配置。
6.3 最后给新手的建议
如果现在让我给一个刚接触 Capacitor 的团队开一张最短行动清单,大概是这三条:第一,先跑通一个最小 Demo,把init -> add -> sync -> run -> build -> release整条链路走一遍,过程中不要去深入插件细节,先把工具的节奏感建立起来;第二,尽早确定权限、签名、服务器协议这些基础设施层面的决策,越晚动越难改;第三,把官方文档当成唯一准绳,社区里的旧答案很多还停留在 Cordova 时代,看着像,用起来全不对。
Capacitor 最让我满意的地方,是它没有试图重新发明一套 UI 或开发范式。它把所有原生差异封装在内,把最终控制权交还给你。你写的还是熟悉的前端代码,但跑出去的是真正能上架的原生应用。对于绝大多数以信息展示和业务流为主的跨平台需求来说,这套基础,够稳,也够省。