关于 capacitor-performance
用于诊断和改善 Capacitor 应用性能的技能,面向排查启动缓慢、卡顿、内存增长或原生调用延迟的开发者。它将 WebView、桥接、原生处理和网络分别分析,并强调在真实设备上测量改动效果。
主要功能
- 01区分 WebView、桥接、原生与网络瓶颈
- 02提供 iOS 和 Android 性能分析参考
- 03指导桥接负载与内存用量排查
- 04检查启动顺序和监听器清理
- 05验证改动前后的性能
适用场景
- 01调查设备上的冷启动缓慢
- 02诊断卡顿或内存增长
- 03减少图像和文件处理延迟
- 04判断延迟主要来自原生调用还是网络等待
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ cap-go/ capgo-skills --skill capacitor-performance安装后在 Claude Code 中输入/capacitor-performance确认已加载。
附带文件metadata.jsonreferences/profiling.md
详细介绍
Capacitor 性能
先找出瓶颈(WebView JS、桥接层、原生端或网络),再针对具体原因进行修复。通用 Web 建议(代码分割、图片压缩、虚拟列表)仍然适用;本技能侧重于 Capacitor 中的不同之处。
适用场景
触发条件:
- 冷启动缓慢、启动画面停留时间过长,或首屏空白数秒。
- 拍照、打开大文件或从后台返回后,应用“重启”或显示白屏。
- 原生调用感觉缓慢,尤其是处理图片、文件或大型 HTTP 响应时。
- 仅在设备上出现滚动或动画卡顿(通常是较旧的 Android WebView)。
- 内存随时间不断增长;监听器越积越多。
不适用场景:
- 崩溃、原生堆栈跟踪 →
debugging-capacitor、capacitor-native-observability。 - 从设备获取日志 →
ios-android-logs。 - 离线存储设计 / SQLite 迁移 →
capacitor-offline-first、sqlite-to-fast-sql。 - 启动画面配置 →
capacitor-splash-screen;键盘调整大小导致卡顿 →capacitor-keyboard。 - OTA 包体积和增量更新 →
capgo-live-updates。
流程
- 在真实设备上使用接近发布版本的构建复现问题。 调试构建会记录每次桥接调用并禁用优化;模拟器无法体现内存和 GPU 限制。
- 使用下方的性能分析表进行分类:JS 主线程繁忙、桥接负载过大、原生线程繁忙,或正在等待网络。
- 检查常见陷阱列表。大多数 Capacitor 特有的性能退化都属于其中一种。
- 一次修复一个问题,并使用相同的命令 / 跟踪记录重新测量。 向用户报告修复前后的数据。
性能分析速查表
| 问题 | 工具 |
|---|---|
| JS 长任务、布局、绘制(iOS) | Safari > Develop > device > app > Timelines。发布构建需要设置 ios.webContentsDebuggingEnabled: true(iOS 16.4 及更高版本默认可检查调试构建)。 |
| JS 长任务、内存堆(Android) | chrome://inspect > Performance / Memory。 |
| 原生 CPU、内存分配、泄漏(iOS) | Xcode > Product > Profile > Time Profiler / Allocations / Leaks;在 Xcode Organizer > Metrics 中查看现场数据。 |
| 原生 CPU / 内存(Android) | Android Studio > Profiler。 |
| Android 冷启动时间 | adb shell am force-stop <appId> && adb shell am start -W -n <appId>/.MainActivity(读取 TotalTime)。 |
| Android 帧统计 | adb shell dumpsys gfxinfo <appId>(卡顿帧百分比)。 |
| Android WebView 版本 | adb shell dumpsys webviewupdate(当前提供程序和版本)。 |
详情和操作指南:references/profiling.md。
Capacitor 特有陷阱
- 通过桥接传输 Base64。 每次插件调用和响应都会通过桥接进行 JSON 序列化。数 MB 的 Base64 字符串会让两个线程都卡住,还可能导致 WebView 进程终止。
- 相机(
@capacitor/camera8.1+ API):使用takePhoto/chooseFromGallery并显示result.webPath;设置targetWidth/targetHeight/quality。除非要上传,否则避免读取完整分辨率的 Base64。较旧的getPhoto/pickImages已弃用。 - 文件:传递路径;将
Capacitor.convertFileSrc(uri)用作<img>/<video>的 src;对于大型读取,使用Filesystem.readFileInChunks;上传和下载使用@capacitor/file-transfer。
- 相机(
- 内存压力下 WebView 进程被终止。 iOS 会记录
WebView process terminated;Capacitor 会重置桥接并重新加载页面,因此内存中的 JS 状态会丢失(看起来像是重启)。Android 会调用onRenderProcessGone;如果未处理,应用会被终止。降低峰值内存占用(参见陷阱 1、大型画布和解码后的图像),持久化关键状态,并在加载时恢复。 - 大响应使用 CapacitorHttp。 设置
plugins.CapacitorHttp.enabled: true后,fetch/XMLHttpRequest会通过原生层路由,响应正文也会经过桥接。它非常适合处理 CORS/ Cookie,但大型下载或流式传输会变慢。大文件请使用 file-transfer。 - 把 Preferences 当作数据库。
@capacitor/preferences(UserDefaults / SharedPreferences)适用于少量键值数据。大型 JSON 块会被整体读写,导致启动变慢;请使用 SQLite(例如@capgo/capacitor-fast-sql)。 - 发布版本中的日志记录。
loggingBehavior默认为debug(仅在调试版本中记录日志)。如果将其设为production,每次桥接调用都会在发布版本中记录日志;请将其改回debug或none。 - 配置中残留开发服务器。 如果发布的构建中
server.url指向局域网或实时重载服务器,应用就会通过网络加载,离线时无法运行。发布前请移除;在 Capacitor 9 中,使用npx cap run <platform> --url进行实时重载,而不是编辑配置。 - 旧版 Android System WebView。 性能和 CSS 支持取决于可更新的 WebView,而不是操作系统版本。
android.minWebViewVersion(默认值为 60)只会显示错误/server.errorPath,不会更新任何内容。在低端 WebView 上,开销较大的 CSS(backdrop-filter,滚动内容上的大型box-shadow/filter)会导致卡顿。 - 监听器泄漏。
addListener返回Promise<PluginListenerHandle>;请等待该 Promise 完成,并在卸载时调用remove()。每次路由更改都重新注册会导致原生回调成倍增加(appStateChange、keyboardWillShow、地理定位watchPosition——使用clearWatch清除)。 - 启动顺序。 仅在首次有意义的内容渲染后隐藏启动画面(
launchAutoHide: false+SplashScreen.hide()),并将非关键插件调用(分析初始化、权限检查)延后到首次绘制之后。请注意,Capacitor 9 将 AndroidlaunchFadeOutDuration的默认值从 200 改为 0。 - 插件导入不是启动开销。 导入
@capacitor/*包会创建轻量级代理;其 Web 实现已通过registerPlugin延迟加载。请延迟加载开销较大的 JS 库(图表、PDF、地图 SDK JS)和路由。
验证
- 使用同一设备、同一构建类型对比前后数据:冷启动
TotalTime(Android),或 Instruments App Launch / Organizer 启动时间(iOS),卡顿帧百分比,以及 Allocations/Profiler 中的峰值内存。 - 对发布配置执行 grep:
grep -nE '"url"|loggingBehavior|webContentsDebuggingEnabled' capacitor.config.*,并确认没有开发用的server.url。 - 对于陷阱 1,搜索完整大小的 Base64 用法:
grep -rnE "resultType: *CameraResultType.Base64|readFile\(|getPhoto\(" src/。 - 将应用切到后台,打开相机 10 次后返回:页面不得重新加载(iOS),应用不得崩溃(Android)。
错误处理
| 消息 / 现象 | 含义 | 解决方法 |
|---|---|---|
WebView process terminated(Xcode 控制台) | WKWebView 内容进程被终止(内存问题) | 陷阱 1/2:降低峰值内存,持久化状态。 |
Android logcat 中的 Render process ... gone / 重负载页面加载后应用退出 | 渲染进程崩溃或被终止 | 降低内存使用量;如需恢复,通过 WebViewListener onRenderProcessGone 实现处理逻辑。 |
ANR in <appId>(logcat) | 主线程阻塞超过 5 s,通常是繁重的插件工作在 UI 线程上执行 | 在自定义插件中将原生工作移出主线程;使用 Android Studio 进行性能分析。 |
Message from debugger: Terminated due to memory issue | iOS jetsam 终止了整个应用 | 使用 Allocations 工具分析;减少图像解码和缓存。 |
logcat 报错,提示 WebView 版本低于 minWebViewVersion | 设备上的 WebView 版本过旧 | 请用户更新 Android System WebView;显示 server.errorPath 页面。 |
参考资料
仅在需要时加载:
references/profiling.md:分步跟踪(Safari Timelines、Chrome Performance、Instruments 模板、Android Profiler、adb 命令)以及如何解读。
评论
支持 Markdown