Skip to main content

SOP 操作手册

本文档是 native-app-multi-rn-bundle 项目的标准操作流程(SOP),面向日常开发、Remote OTA 发版、原生壳维护与故障排查。技术背景见项目根目录 README.mdplatform-design.mdmulti-bundle.mddynamic-multi-bundle.md团队分工、依赖分层、Capability Requestcollaboration.md


1. 适用范围与角色

1.1 项目结构

目录职责
rn_app/React Native 生产者(BrownfieldLib / XCFramework)
ios_native/原生 iOS 壳(NavigationStack + RN 入口)
bundle-server/Remote 入口 manifest、bundle 上传与 OTA 服务
scripts/共享构建脚本(如 Debug 壳导出)

1.2 角色分工

角色职责频率
壳维护者打 Debug/Release 原生包、导出共享模拟器 .app、升级原生依赖原生依赖变更 / 壳 UI 变更时
RN 开发者改 JS/TS、npm start、Metro 热重载日常
Remote 业务开发者screens/remote/、发 OTA 子 bundle按需
服务端维护者运维 bundle-server、上传/回滚 bundle、管理 manifest发版 / 运维时

完整协作机制(平台区/业务区、依赖 L1/L2/L3、禁止自建 RN 工程)见 collaboration.md

1.3 两种 RN 入口(勿混淆)

产品名称技术实现是否 OTA
核心 RN(Scheme 1)单 bundle + 多 moduleName
远程业务(Remote)Split Bundle + manifest + FeatureHost

2. 环境准备(首次)

2.1 前置条件

  • macOS + Xcode(iOS 模拟器)
  • Node.js ≥ 22.11.0
  • CocoaPods(rn_app/ios 首次需 pod install

2.2 克隆与安装

# 根目录
git clone <repo-url>
cd native-app-multi-rn-bundle

# RN 工程
cd rn_app
npm install
cd ios && pod install && cd ..

# bundle-server(Remote / OTA 开发时需要)
cd ../bundle-server
npm install

2.3 首次打包 RN + 链接 SPM(壳维护者,只需一次)

cd rn_app
npm run brownfield:package:ios # Release;开发用 debug 见下文

Xcode 操作:

  1. 打开 ios_native/ios_native.xcodeproj
  2. File → Add Package Dependencies… → Add Local…
  3. 选择 rn_app/ios/.brownfield/package/build
  4. target ios_nativeGeneral → Frameworks 添加 BrownfieldLib

2.4 验证安装成功

  1. 终端 1:cd rn_app && npm start
  2. Xcode:scheme ios_nativeDebug、Run 到模拟器
  3. 应看到原生菜单:首页 / 个人中心 / 设置 / React Native / 远程业务

3. SOP-A:日常 RN 开发(核心页 + Metro 热重载)

适用: 改 Scheme 1 核心页(HomeScreen / ProfileScreen / SettingsScreen)或主 bundle 逻辑。

步骤

步骤操作负责人
A-1cd rn_app && npm startRN 开发者
A-2Xcode Debug Run ios_native(或已安装的共享 Debug 壳)RN 开发者
A-3修改 rn_app 内 JS/TS,保存后观察模拟器热重载RN 开发者

检查清单

  • scheme 为 Debug(非 Release)
  • BrownfieldLib 为 Debug 包(npm run brownfield:package:ios:debug
  • Metro 终端无报错,模拟器 App 已启动
  • 未误用 cd rn_app && npm run ios(那是独立 RN App,不是 brownfield 壳)

热重载不生效时

cd rn_app
npm run brownfield:package:ios:debug
# Xcode:Product → Clean Build Folder,再 Run

订单 React Navigation 原生依赖(screens / gesture-handler)

order 多级页使用 @react-navigation/native-stack,BrownfieldLib 必须包含 react-native-screensreact-native-gesture-handler

cd rn_app
npm install # 更新 package.json 后
cd ios && pod install && cd ..
npm run brownfield:package:ios:debug:sim # 模拟器 Debug 包
# Xcode:重新选择 ios/.brownfield/package/build 中的 BrownfieldLib → Clean → Run

未重建 BrownfieldLib 时典型报错:unimplemented component: <RNSScreenStack>RNGestureHandlerModule not found

Remote 退出原生(NativeShellNavigation)

Remote 根页「← 菜单」调用 NativeShellNavigation.popToNative()(BrownfieldLib)。新增该模块后同样需重建 BrownfieldLib + Xcode Clean Build。


4. SOP-B:Remote 业务日常开发(Metro 模式)

适用:screens/remote/screens/remote/components/,需要 HMR。

步骤

步骤操作
B-1终端 1:cd rn_app && npm start
B-2(可选)终端 2:cd bundle-server && USE_METRO_BUNDLES=true npm run dev
B-3Xcode Debug Run ios_native
B-4原生壳工具栏选择 Metro 模式
B-5进入「远程业务」→ 订单 / 活动页,改 screens/remote/ 验证 HMR

重要规则

目录用途是否 Metro HMR
screens/remote/日常 UI 开发
bundles/ota_*/仅 build/upload

禁止:src/screens/remote/import bundles/ota_*

发版前可跑:

cd rn_app && npm run verify
# 或单独:verify:ota-scope / verify:native-deps

verify:native-deps 检测生产依赖树中 未批准的 native npm 包;见 collaboration.md


5. SOP-C:Remote OTA 发版

适用: 将 Remote 子 bundle(含公共 shared split + 各 feature split)发布到 bundle-server,供 App OTA 模式加载。

OTA 采用 shared segment 0 + feature segment 1 两段式 split:客户端先 load ota_shared,再 load order / promo。Manifest 顶层含 sharedBundle 字段。详见 dynamic-multi-bundle.md

5.1 发布前检查

  • UI 改动已在 Metro 模式验证
  • 版本号符合 semver(如 0.0.7);shared / order / promo 同一发版应使用相同版本号
  • bundle-server 可访问
  • 首次部署或 DB 无 shared 入口时,需 npm run db:seed(见 5.2)
  • 若只改共享 UI(components/),OTA 包装层无特殊改动时可只 rebuild

5.2 完整发版流程(推荐)

# 1. 构建(日常 dev 版本号用 --dev;正式发版用 build:bundles)
cd rn_app
npm run build:bundles:dev # 或 npm run build:bundles
# 产物:dist/bundles/ota_shared.<version>.ios.jsbundle
# dist/bundles/ota_order.<version>.ios.jsbundle
# dist/bundles/ota_promo.<version>.ios.jsbundle

# 2. 启动 bundle-server(首次需 seed shared 入口)
cd ../bundle-server
npm run db:seed # 首次或清理 DB 后;seed shared / order / promo
npm run dev

# 3. 上传 shared + 各 feature(版本号与构建一致,如 0.0.7)
./scripts/upload-bundle.sh shared 0.0.7 ../rn_app/dist/bundles/ota_shared.0.0.7.ios.jsbundle
./scripts/upload-bundle.sh order 0.0.7 ../rn_app/dist/bundles/ota_order.0.0.7.ios.jsbundle
./scripts/upload-bundle.sh promo 0.0.7 ../rn_app/dist/bundles/ota_promo.0.0.7.ios.jsbundle

# 4. 校验 manifest
curl -s http://127.0.0.1:3001/api/manifest | jq '{sharedBundle, features: [.features[] | {id, version, sizeBytes}]}'

npm run dev 会自动 prisma db pushdb:seed 在已有数据时通常可跳过,但 首次 或重置 DB 后必须执行以注册 shared 入口。

5.3 上传(二选一)

方式 1 — Admin UI(推荐)

  1. 打开 http://127.0.0.1:3001/admin
  2. 依次上传 sharedorderpromo(或需更新的入口)
  3. 填写相同 semver,上传对应 .jsbundle
  4. 勾选「上传后立即上线」,或在历史版本中 设为线上

方式 2 — 命令行

cd bundle-server
./scripts/upload-bundle.sh shared 0.0.7 ../rn_app/dist/bundles/ota_shared.0.0.7.ios.jsbundle
./scripts/upload-bundle.sh order 0.0.7 ../rn_app/dist/bundles/ota_order.0.0.7.ios.jsbundle
./scripts/upload-bundle.sh promo 0.0.7 ../rn_app/dist/bundles/ota_promo.0.0.7.ios.jsbundle

5.4 客户端验证

步骤操作
C-1确认 bundle-server 运行:cd bundle-server && npm run dev
C-2原生壳切 OTA 模式
C-3下拉刷新 Remote 菜单(如需)
C-4进入订单页,确认显示 OTA 标识 / 新版本内容
C-5(可选)等待 20s polling 或点页面内「检查更新」验证 Banner

5.5 回滚

Admin: 历史版本 → 设为线上 / 回滚

API:

curl -X POST http://127.0.0.1:3001/api/features/order/rollback \
-H 'Content-Type: application/json' \
-d '{"releaseId": "<release-id>"}'

5.6 OTA 仍显示旧内容

删除沙盒缓存或重装 App:

DocumentDirectory/rn-bundles/

6. SOP-D:bundle-server 运维

6.1 启动服务

cd bundle-server
npm run dev # 开发:db:prepare + 热重载
# 或
npm run build && npm start # 生产
地址说明
http://127.0.0.1:3001/admin管理后台
http://127.0.0.1:3001/api/manifestmanifest JSON

数据库:bundle-server/data/bundle-server.db
上传 bundle 存储:bundle-server/data/bundles/(与数据库同目录,重启后保留)

6.2 环境变量

变量默认说明
PORT3001服务端口
USE_METRO_BUNDLESfalsemanifest 指向 Metro split bundle
METRO_HOSThttp://127.0.0.1:8081Metro 地址

6.3 冒烟测试

先启动 server,再另开终端:

cd bundle-server
npm run smoke:manifest

# 需先 build bundles
cd ../rn_app && npm run build:bundles
cd ../bundle-server && npm run smoke:e2e

6.4 禁用 Remote 入口

Admin 禁用,或:

curl -X POST http://127.0.0.1:3001/api/features/promo/toggle \
-H 'Content-Type: application/json' \
-d '{"enabled": false}'

原生壳下拉刷新 Remote 菜单生效。


7. SOP-E:新增 Remote 入口

步骤操作文件/位置
E-1添加 Metro dev 页面screens/remote/<Name>Screen.tsx
E-2共享 UI 放 componentsscreens/remote/components/
E-3注册 moduleNamescreens/remote/featureMeta.ts
E-4新建 OTA 入口bundles/ota_<id>/index.js
E-5OTA 包装页bundles/ota_<id>/screens/
E-6注册服务端入口Admin 或 prisma/seed.ts(含 shared 若为新环境)
E-7加入构建列表scripts/build-bundles.jsbundles 数组
E-8构建并上传npm run build:bundles → upload shared + feature

日常开发只改 screens/remote/;OTA 包装仅在需要不同 badge/文案时改 bundles/ota_*/screens/


8. SOP-F:原生壳维护与团队分发

8.1 何时需要重新打壳

  • rn_app 增删/升级原生依赖(新 pod、RN 大版本)
  • 修改 ios_native 原生代码
  • 升级 @callstack/react-native-brownfield 大版本
  • 修改 SplitBundleLoader 等原生模块

只改 JS/TS → 不需要重发壳。

8.2 Debug 包(开发连 Metro)

cd rn_app
npm run brownfield:package:ios:debug
# Xcode Clean + Run

8.3 Release 包(内嵌 JS,无 Metro)

cd rn_app
npm run brownfield:package:ios

8.4 导出共享模拟器 App

chmod +x scripts/build-debug-shell.sh
./scripts/build-debug-shell.sh
# 产物:dist/ios_native-debug-simulator.app

8.5 同事安装与开发

# 安装壳(模拟器已启动)
xcrun simctl install booted /path/to/ios_native-debug-simulator.app

# 开发
cd rn_app && npm install && npm start
# 模拟器桌面打开 ios_native

同事不需要 Xcode、pod、brownfield 打包。


9. SOP-G:发版前完整验证(Release 路径)

步骤操作预期
G-1npm run brownfield:package:iosBrownfieldLib Release 产物更新
G-2npm run build:bundlesshared + 子 bundle + hash 正常
G-3upload shared + 全部 Remote 入口manifest sharedBundle + features version/hash 更新
G-4npm run smoke:e2e服务端冒烟通过
G-5Xcode Release Run核心页正常
G-6OTA 模式进 Remote 页split load 成功,无 useSyncExternalStore 崩溃
G-7断网进 Remote 页fallback 到主 bundle 内置页

10. 故障排查速查

现象可能原因处理
改 JS 不更新Release 包 / Release schemebrownfield:package:ios:debug + Debug scheme + Clean
Metro No apps connectedApp 加载内嵌 bundle同上
RCTStatusBarManager 崩溃缺 Info.plist 配置确认 UIViewControllerBasedStatusBarAppearance = false
Remote Metro 模式无 HMR改错目录screens/remote/,非 bundles/ota_*
OTA 模式仍旧版沙盒缓存DocumentDirectory/rn-bundles/ 或重装
OTA 切换后白屏registry 未清切换模式后重进页;见 fixes 文档
manifest 拉不到server 未启 / IP 不对启动 server;真机改局域网 IP
split load 崩溃eval 完整 bundle必须用 ota_*.ios.jsbundle + SplitBundleLoader
smoke 失败server 未运行cd bundle-server && npm run dev
OTA 503 后恢复服务端 bundle 文件短暂缺失 / 重启客户端自动退避重试(最多 10 次);manifest 暂为 sha256:unset;恢复 upload 或还原 data/bundles/ 后重进页
OTA 404 不重试release 已从 DB/磁盘删除立即失败;有本地 cache 则继续用;无 cache → 错误页提示 re-upload
manifest hash unsetDB 有 release 但磁盘无文件Admin 重新 upload 或检查 data/bundles/;勿与 503 transient 混淆

详细修复记录:docs/fixes/README.md(索引 + 面试叙事 stories/


11. 命令速查

# Metro
cd rn_app && npm start

# Debug / Release 打包
cd rn_app && npm run brownfield:package:ios:debug
cd rn_app && npm run brownfield:package:ios

# Remote 子 bundle
cd rn_app && npm run build:bundles:dev # 日常;正式发版用 build:bundles
cd rn_app && npm run verify

# bundle-server
cd bundle-server && npm run db:seed # 首次或重置 DB 后
cd bundle-server && npm run dev
cd bundle-server && USE_METRO_BUNDLES=true npm run dev
cd bundle-server && ./scripts/upload-bundle.sh shared 0.0.7 ../rn_app/dist/bundles/ota_shared.0.0.7.ios.jsbundle
cd bundle-server && ./scripts/upload-bundle.sh order 0.0.7 ../rn_app/dist/bundles/ota_order.0.0.7.ios.jsbundle
cd bundle-server && ./scripts/upload-bundle.sh promo 0.0.7 ../rn_app/dist/bundles/ota_promo.0.0.7.ios.jsbundle
curl -s http://127.0.0.1:3001/api/manifest | jq '{sharedBundle, features: [.features[] | {id, version, sizeBytes}]}'

# 共享 Debug 壳
./scripts/build-debug-shell.sh

12. 相关文档

文档内容
项目根目录 README.mdBrownfield 集成与日常开发
multi-bundle.md多 Bundle 方案选型
dynamic-multi-bundle.mdRemote + OTA 架构详解
case-study-remote-ota-metro-isolation.mdMetro / OTA 隔离案例
工程仓 rn_app/screens/remote/README.mdRemote Metro 开发说明
工程仓 bundle-server/README.md服务端 API 与命令

最后更新:2026-07-09