CalcGuide · 技术博客主页 / 一页纸学习计划
🟠

跨端性能、原生桥、离线同步、测试与发布

分类:前端与客户端 · 路径:docs/topics/cross-platform-performance-and-platform-bridges/README.md

#performance#jsi#native-modules#offline#detox#maestro#app-store#play-console

用 2~3 周从冷启动拆解到能交付一份 TestFlight / Play 内部测试发布 + e2e + 离线同步

父主题

移动端跨平台开发:从原生 iOS/Android 到 React Native / Flutter 与原生桥

子主题(0)

跨端性能、原生桥、离线同步、测试与发布

0. 元信息

1. 学习路线

启动时间拆解(Native init / JS bundle / 首帧)
  → 帧时间与掉帧(Choreographer / VSync / Reanimated / Impeller)
  → 内存与 OOM(Leak / Bitmap / Image 缓存)
  → 原生桥深化(JSI / TurboModule / Platform Channel / Codegen)
  → 离线存储(Realm / SQLite / Drift / MMKV / Hive)
  → 离线同步与冲突解决(时间戳 / CRDT / OT)
  → E2E 测试(Detox / Maestro / XCTest / Espresso)
  → 签名、Provisioning / Keystore
  → App Store / Play Console 发布、审核、监控

2. 阶段周数分配(46 周,每天 1.52 小时)

精简子主题按”性能 → 原生桥 → 离线 → 发布”四段推进。

阶段主题周数备注
1启动与帧时间调优1 周Native init / JS bundle / VSync
2内存与 OOM0.5 周Leak / Bitmap / Image 缓存
3原生桥深化1 周JSI / TurboModule / Platform Channel
4离线存储与同步1.5 周Realm / SQLite / MMKV + CRDT
5E2E 测试0.5 周Detox / Maestro
6签名与发布0.5 周Provisioning / Keystore / Store
7综合项目(性能优化实战)1 周含 README + 复盘

选 4 周方案时把第 4/5 阶段压成 1 周。每周留 0.5 天复盘。

3. 九阶段表

阶段核心知识实践产出可观察学会标准
1启动时间拆解一份冷启动时间报告(Native / JS bundle / 首帧)冷启动 < 1.5s
2帧时间与掉帧Reanimated / Impeller 调优P99 帧时间 < 16ms
3内存与 OOMLeakCanary / Instruments Allocations内存峰值 < 300MB
4原生桥一个自定义 TurboModule + 一个 Platform Channel跨端调原生相机
5离线存储Realm / SQLite / Drift 任选1 万条数据读写 < 50ms
6离线同步时间戳 + LWW / CRDT冲突可解释
7E2E 测试Detox / Maestro 用例 ≥ 10 条接入 CI
8签名Provisioning / Keystore / Play App Signing真机安装
9发布与监控TestFlight / Play 内部测试 + Crashlytics / Sentry邀请链接

关键陷阱:只看平均延迟;同步阻塞跑大文件;忽略 ATS / cleartext;Privacy Manifest 不填;签名丢失;审核驳回不复盘。

4. 第一周任务

任务当天交付
Day 1用 Flipper / DevTools 拆冷启动:Native init + JS bundle 加载 + 首帧启动时间报告
Day 2写 Reanimated 3 worklet 60fps 列表 + 用 FlashList / ListView.builder帧时间对比
Day 3用 Instruments / LeakCanary 跑内存,找一张 1MB Bitmap 不释放内存报告
Day 4用 Codegen 写 TurboModule 调 iOS / Android 原生相机相机 Demo
Day 5用 MethodChannel 调原生 Toast / Push双端桥 Demo
Day 6用 Realm / SQLite 存 1 万条数据,测读写延迟存储性能
Day 7步骤 A:跑通跨端最小可用(含离线 + 原生桥 + e2e);步骤 B:补齐 4 类边界(冷启动 > 2s、JS bundle > 5MB、内存峰值 > 500MB、e2e 失败)综合 Demo + 边界测试

5. 阶段通用验收

  1. 不看答案独立写出冷启动拆解报告与 e2e 用例;
  2. 用自己的话解释 Native init / JS bundle / 首帧的关系;
  3. 画一张图:冷启动到首帧的时序;
  4. 测试正常路径、冷启动慢、帧时间差、内存峰值高;
  5. 至少准备 3 组自定义数据并贴出实际渲染;
  6. 记录冷启动、P99 帧时间、内存峰值、JS bundle 大小 4 个核心指标;
  7. 能修改既有 App 加原生桥、加离线缓存、加 e2e。

6. 最终验收

7. 综合项目

用 RN New Architecture 或 Flutter 3 实现一个含原生相机 + 离线同步 + e2e + 性能报告 + TestFlight / Play 发布的 Todo / 电商 / IM App。成果即父主题首选综合项目。

本主题贡献

跨端生产化意味着把帧时间、离线冲突、e2e、签名四件事都打通,单点优化不够。本子主题负责讲清”冷启动怎么拆”、“Realm/SQLite 怎么选”、“Detox vs Maestro 怎么取舍”、“Fastlane match 怎么管签名”四个跨端 QA 必答题,并把 JSI / TurboModule / Codegen 三件套在性能优化场景下如何替代 Bridge 串成完整证据链。

3 职责

  1. 把冷启动拆成 Native init / JS bundle / 首帧三段,给每个 SDK 加一次 P50/P99 时间预算(< 1.5 s)。
  2. 用 Realm / SQLite / Drift / MMKV / Hive 五种存储做 1 万条数据读写延迟对比,选出离线 + 同步场景的合理组合。
  3. 用 Detox(iOS / Android 原生)+ Maestro(跨 UI)写 e2e ≥ 10 条,用 Page Object + 显式等待接 CI。

4 交付物

  1. 一份冷启动时间报告(Native init / JS bundle / 首帧 / 看板 SDK 各占时长),含 P50 / P99 与 SDK 启动占比排行。
  2. 一份 Realm 离线同步 demo(时间戳 + deviceId + LWW 冲突解决),含两端同改同写的复现记录与 CRDT 升级路径。
  3. 一份 Detox + Maestro 双栈 e2e(login / list / detail / offline),接入 GitHub Actions / EAS Build,含 Page Object 模式用例。
  4. 一份 Fastlane match 签名 + TestFlight / Play 内部测试发布记录(含 dSYM / mapping 上传 + Privacy Manifest + ATS 配置)。

3 指标

  1. 冷启动 P99 < 1.5 s(Flipper / DevTools Timeline 三段总和)。
  2. 列表 P99 帧时间 < 16 ms(长列表 + FlashList / ListView.builder + Reanimated)。
  3. e2e 接入 CI 后 flake rate < 5%(重试一次通过 / 总用例数)。

App Store Review GuiPlay Console PoliciesDetox DocumentationMaestro DocumentationRealm DocumentationFastlane Documentation。复制前核对 LICENSE。

8. 推荐资料

App Store Review GuiPlay Console PoliciesDetox DocumentationMaestro DocumentationRealm DocumentationFastlane Documentation。复制前核对 LICENSE。

9. 学习资料汇聚(v0.3 自包含)

9.1 背景与动机

跨端 App 真正难的是「生产质量」。能跑通 Demo 不等于能上 App Store。冷启动、帧时间、内存峰值是体验三件套;离线冲突是分布式系统的简化版;签名与审核是平台红线;e2e 是回归防线。这四件事一起决定一个跨端 App 能不能活过第一个版本。

9.2 概念地图

flowchart TB
  Perf[性能三件套] --> Cold[冷启动]
  Perf --> Frame[帧时间]
  Perf --> Mem[内存]
  Bridge[原生桥] --> JSI[JSI / TurboModule]
  Bridge --> Channel[Platform Channel]
  Offline[离线] --> Store[Realm / SQLite / Drift / MMKV]
  Offline --> Sync[同步协议: LWW / CRDT / OT]
  Test[E2E 测试] --> Detox
  Test --> Maestro
  Test --> CI[CI 接入]
  Publish[发布] --> Signing[签名: Provisioning / Keystore / Play App Signing]
  Publish --> Store[App Store Connect / Play Console]
  Publish --> Crash[Crashlytics / Sentry / Datadog Mobile]

9.3 基础知识讲解

9.3.1 论文 / 规范

资料用法
Apple, App Store Review Guidelines审核红线
Google, Play Console Policies上架规则
W3C, Trace Context Level 1跨进程 trace
IETF RFC 8446, TLS 1.3ATS 对照

9.3.2 书

用法
Continuous Mobile(Leanpub)移动 CI/CD
Charity Majors, Observability Engineering移动 APM

9.3.3 博客 / 文档

资料用法
Detox DocumentationiOS / Android E2E
Maestro Documentation跨 UI E2E
Sentry Mobile错误监控
Fastlane Documentation签名与发布
Realm Documentation移动数据库
Brendan Gregg BlogiOS Instruments 调优

9.3.4 人物

人物关注点
Charity Majors可观测工程化
Brandon WilliamsEAS / Expo
Marc RousselMaestro / mobile.dev
Laurent GasserDetox / Wix

9.3.5 方法

9.3.6 重点训练材料

9.4 经典问题与经典案例

#问题最简答案
1冷启动 > 2s拆 Native init / JS bundle / 首帧;减 SDK
2列表掉帧换 FlashList / ListView.builder;用 Reanimated
3内存峰值 > 500MBImage 缓存上限;Bitmap 复用
4原生桥大对象慢JSI 同步;Codegen 免序列化
5离线冲突丢数据时间戳 + 设备 ID + LWW;或 CRDT
6e2e 不稳定Page Object + 显式等待
7Provisioning 过期用 Fastlane match 统一管理
8Keystore 丢失用 Play App Signing
9Privacy Manifest 不填iOS 17+ 必填
10Crashlytics 不上报看 dSYM / mapping 文件是否上传

9.5 学习难点

难点为什么会卡突破路径
冷启动拆解多个 SDK 串联用 Flipper / DevTools Timeline
JSI 大对象序列化慢用 SharedArrayBuffer 或直传引用
离线冲突业务场景多样用 LWW 起步,按业务换 CRDT
e2e 稳定性时序敏感用 Page Object + 显式等待
签名管理Provisioning 复杂用 Fastlane match
审核驳回政策更新频繁关注 Apple News + 提前自查

9.6 技术标准与接口

9.6.1 Entity

名称版本组织状态
Detox20.xWix活跃
Maestro1.xmobile.dev活跃
Realm12.xMongoDB活跃
MMKV2.xTencent活跃
Drift2.xSimon Binder活跃
Fastlane2.xFastlane活跃
CrashlyticsFirebase活跃
Sentry MobileSentry活跃

9.6.2 Scope

原生桥负责跨端与原生 API 互调;离线存储与同步负责数据可用性;E2E 负责回归防线;签名与发布负责平台合规;监控负责故障响应。

9.6.3 Structure

必会接口:JSI jsi::Runtime、Platform Channel MethodChannel / EventChannel、Realm Realm.open、Detox by.id / tap()、Maestro tapOnfastlane match / fastlane supply

9.6.4 Ecosystem

CI:Bitrise、EAS Build、GitHub Actions + Fastlane;测试:Detox / Maestro / XCTest / Espresso / Patrol;监控:Sentry / Crashlytics / Datadog Mobile RUM / OpenTelemetry Mobile SDK。

9.6.5 Depth Tiers

层级能力
L0知道性能 / 桥 / 离线 / 测试 / 发布
L1能读懂 Detox / Maestro 用例
L2能写跨端 App + 原生桥 + 离线
L3能写 e2e + 跑发布 + 接监控
L4能设计 CI/CD + 多地域灰度 + 隐私合规

本子主题目标:L3

9.6.6 Source

10. 常见误区

11. 所有知识点分类(统一规则)

  1. 编程语言;2. 数据结构与算法;3. 计算机基础;4. 工程技术;5. Web 与后端;6. 前端与客户端;7. 数据与人工智能;8. 项目与职业能力;9. 安全与可靠性。

本计划归属:前端与客户端 主 + 工程技术 / 安全与可靠性 辅。

代码块 1:冷启动到首帧时序

sequenceDiagram
    participant Process
    participant Native
    participant Engine as RN Engine / Flutter Engine
    participant Bridge as JS bundle / Dart code
    participant UI as UI 渲染
    Process->>Native: init
    Native->>Engine: 启动引擎
    Engine->>Bridge: 加载 JS bundle / Dart isolate
    Bridge->>UI: 首帧渲染
    UI-->>Process: 首帧完成
    Note over Process,UI: 冷启动 = T0 → 首帧

代码块 2:Detox E2E

// e2e/login.test.ts
import { by, device, element, expect } from 'detox';

describe('Login', () => {
  it('should login', async () => {
    await element(by.id('email')).typeText('a@b.com');
    await element(by.id('password')).typeText('hunter2');
    await element(by.id('submit')).tap();
    await expect(element(by.id('home'))).toBeVisible();
  });
});

代码块 3:Maestro E2E

# .maestro/login.yaml
appId: com.example.demo
---
- tapOn:
    id: email
- inputText: a@b.com
- tapOn:
    id: password
- inputText: hunter2
- tapOn:
    id: submit
- assertVisible:
    id: home

代码块 4:Realm 离线同步(时间戳 + LWW)

// src/sync/realm.ts
import Realm from 'realm';

class Todo extends Realm.Object<Todo> {
  _id!: string;
  title!: string;
  updatedAt!: number;
  deviceId!: string;

  static schema: Realm.ObjectSchema = {
    name: 'Todo',
    primaryKey: '_id',
    properties: {
      _id: 'string',
      title: 'string',
      updatedAt: 'int',
      deviceId: 'string',
    },
  };
}

export function resolveConflict(local: Todo, remote: Todo): Todo {
  return local.updatedAt >= remote.updatedAt ? local : remote;
}

代码块 5:Fastlane 发布

# fastlane/Fastfile
default_platform(:ios)

platform :ios do
  lane :beta do
    match(type: 'appstore')
    build_app(workspace: 'ios/Demo.xcworkspace', scheme: 'Demo')
    upload_to_testflight(skip_waiting_for_build_processing: true)
  end
end

直接依赖(4)

查看知识图谱