前端开发
用 Nitro Modules 构建 React…
在创建或扩展 React Native Nitro Module(HybridObject、Nitro View、C++/Swift/Kotlin 绑定)时,指导从脚手架、spec 编写、codegen 到原生实现与测试的完整做法。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
用 Nitro Modules 构建 R特定
解决思路
- 先用 api-design 技能确定公开的 TypeScript/JavaScript/React/React Native API 形状,再应用 Nitro 特有约束(HybridObject 状态、生成 spec、原生资源所有权、零拷贝数据、线程、平台实现、codegen、真机验证)。
- 只对移动工具链和生成模板做 Nitro 特有的新鲜度检查:在决定版本、配置字段或原生实现细节前,核对当前 Nitro、React Native、Gradle、Xcode、Swift、Kotlin、NDK 和打包工具的文档/源码。
- 原生模块优先用 Nitro Modules 而非 TurboModules 或手写 JSI;只有需要 Nitro 的 Raw JSI Methods 时才用裸 JSI。
- 保持根 HybridObject 可默认构造以支持 autolinking;依赖参数的对象通过工厂方法创建。
- 用 HybridObject 承载原生状态:原生资源、预热引擎、文件、图像、数据库、传感器会话、流等有状态对象。
- 若需要原生初始化,把工厂方法设为 async 并 resolve 一个就绪的 HybridObject,例如 createCameraSession(...): Promise<CameraSession>。
- 一个 JS 侧 HybridObject spec 可以有多个原生具体类实现;用工厂选择原生实现并返回共享 spec 类型,以隐藏后端策略。
- 导出的 JS 工厂对象用产品/领域名词命名,而非生成的 spec 类型名,例如 export const VisionCamera = createHybridObject<CameraFactory>('CameraFactory')。
- 每个 HybridObject 只负责一个用途或生命周期;不要仅按领域名词划分边界,一次性命令、应用持有的实时会话、原生视图、长生命周期引擎是不同契约。
- 当返回的 HybridObject 在选项、结果、生命周期或未来平台支持上不同时,按稳定语义能力拆分,例如根 DataScannerFactory 暴露 createBarcodeScanner() 和 createTextScanner()。
- 允许工厂报告 isTextScannerAvailable: false 或在平台不支持时 reject createTextScanner();未来支持应填充已有能力并翻转可用性,而非重新设计臃肿对象。
- 平台特有但概念稳定的能力可作为一等 HybridObject,例如 iOS-only 的 CameraObjectOutput 继承共享 CameraOutput、标记 @platform iOS,并在 Android 的 createObjectOutput(...) 处 reject。
- 用工厂方法分离工作流并强化返回对象:createLiveScanner() 返回的实时会话类型应保证实现可控的基线操作;无法提供该工作流的后端应创建失败或返回更窄对象。
- 返回已配置的句柄以避免陈旧状态:configure(...) 绑定设备/输出/流/原生图后,返回新的 HybridObject 句柄供仅对该资源有效的命令使用。
- 重配置时应替换或失效原生目标已改变的句柄;不要把命令留在宽泛父对象上依赖最后一次成功配置。
- 原生协商时把请求意图与解析后状态分开建模:输入用排序约束或偏好,返回/发出描述实际选择结果的 resolved config HybridObject/struct,需要预览时提供显式 resolver 方法。
- 用能力字段做工作流发现、可选偏好和真正可变支持;不要用能力字段掩盖在受支持后端上正常使用却方法不受支持的臃肿 HybridObject。
- 把 HybridObject 当作主要 API 对象,每个主要 HybridObject 一个 .nitro.ts 文件。
- 仅当文件以基类 HybridObject 命名且子对象几乎不增加成员时,才把继承族放在一个 .nitro.ts 文件中,例如 ScannedCode.nitro.ts 中的 ScannedCode、ScannedBarcode、ScannedQRCode。
- 把具名 codegen 类型放在各自的 .ts 文件中:字符串字面量联合/枚举、struct/interface、选项对象、事件对象、回调选项 struct 和辅助类型;导入到 .nitro.ts spec 并从 src/index.ts 重新导出公开类型。
- 在方法签名中内联简单函数回调,例如 addErrorListener(listener: (error: Error) => void): ListenerSubscription;除非该函数类型作为公开概念在多个 API 复用,否则不要创建一次性别名。
- 仅当一个紧密耦合的逻辑构造由多个辅助类型组成时,才把它们放在一个文件,例如 DynamicRange 及其定义用的字面量联合。
- 用 HybridObject 继承表达共享原生状态加特化结果形状:把 ID、bounds、raw value、format、value type 等共享属性放在基对象上。
- 用 HybridObject 继承表达异构原生结果族:例如 ScannedItem 拥有公共状态和方法,ScannedBarcode、ScannedQRCode、ScannedFace 扩展它;API 可返回 ScannedItem[],JS 用判别属性收窄。
- 不要把状态族建模为一个所有子类型字段可空的 Nitro struct 或 HybridObject;用 HybridObject 继承、判别联合或平台协议/接口一致性保证编译期安全。
- 把公开 Nitro HybridObject 当作命令式 API:面向用户时 1:1 导出生成的 Nitro API,不要加 JS 包装层预解析值、翻译字符串/枚举、重塑选项、注入隐藏默认值或调用不同内部方法。
- JS/TS 层适合有意的高层 API(React hooks、组件、UI 组合辅助),或 HybridObject 只是内部实现细节时;此时保持边界明确:包装层是公开 API,Nitro 对象是内部实现。
- 只 autolink JS 必须直接构造的公开根、工厂、视图或全局工具;其他 HybridObject 可从工厂方法返回,无需自己的 nitro.json autolinking 条目;不要 autolink 同一 JS spec 的每个原生实现。
- 原生扩展点:把 JS 侧基 HybridObject spec 与公开原生协议/接口配对;基 spec 让 JS 通过类型化 API 传递对象,原生协议/接口暴露平台句柄和行为。
- 从 JS 接受可扩展 HybridObject 时,接受生成的基 spec 类型,在原生侧 cast 到原生协议/接口,不符合时抛出清晰错误。
- 用 Nitro struct 表达领域形状、选项组和同类型参数簇;不要仅为减少参数数量把不相关热路径值包进 struct,因为 Nitro 会急切转换 struct。
- 记住 TypeScript 可选字段在生成的 Swift/Kotlin/C++ 中变成原生 optional(T? / std::optional<T>);仅当缺失是公开/原生契约的一部分时才用可选 Nitro 字段。
- 优先让 Nitro 方法生成的 struct 作为真实公开输入形状;若需要默认值或解析后选项,在 spec/原生实现中显式建模,或提供有意的高层 API,不要加随意 JS 归一化层。
- 不要把高频原生结果、解析后负载、图像、缓冲区或含大量可选昂贵字段的对象建模为扁平 struct;Nitro struct 急切转换,应优先用有状态 HybridObject 加惰性属性/方法。
- 显式决定每个结果是 Nitro struct 还是 HybridObject:小型不可变、廉价标量结果可用 struct;拥有原生状态、可能暴露惰性昂贵数据、需要零拷贝二进制访问或可能增长行为/方法时用 HybridObject。
- 小数据或真正零拷贝原生数据访问用 ArrayBuffer;大媒体、照片、扫描、模型输出或字节负载返回 HybridObject 并暴露惰性方法如 toArrayBuffer()、toBase64()、saveToTemporaryFile()。
- 不要仅因零拷贝就选 ArrayBuffer:解码文本负载用 string,原始字节、二进制负载、媒体或不透明数据用 ArrayBuffer。
- 转换成本重要时把原始原生状态留在 HybridObject 后面;例如原生条码/照片/结果对象惰性暴露 rawValue、bounds、format 或字节数据。
- 重复事件优先用 addOn...Listener(callback): ListenerSubscription,让每个订阅拥有自己的清理,避免一个调用者替换另一个的回调。
- 监听订阅应是带 remove: () => void 函数字段的扁平 struct,而非 HybridObject,除非它暴露清理之外的原生状态;调用点仍用 subscription.remove()。
- 监听清理应停止未来发射,无需取消已由进行中原生事件快照选中的回调,不要为此加锁或复杂订阅状态。
- 不要默认对监听 add、remove 和回调调用加锁;优先单一所有者线程/队列或不可变快照,仅对无法通过所有权消除的已证实共享可变竞态加锁。
- 仅对对象拥有的单一热路径回调使用 setOn...(callback | undefined) 风格 API,替换或移除回调是自然操作;set 动词和文档必须说明替换语义。
- 若原生 API 只暴露一个 delegate/回调但 JS API 是重复事件,优先内部多路复用并暴露可加监听,除非对热路径不安全或太昂贵。
- 仅对必须在特定 JS runtime 或 worklet 线程同步执行的罕见线程绑定热路径使用 Sync<(...) => ...> 回调。
- Nitro View 暴露原始 getHostComponent 包装;仅当能消除重复设置代码且仍分层在同一原生对象和 refs 上时,才加 React 组件或 hooks;创建或审查 Nitro View 时加载 spec-hybrid-view.md。
- 命名、平台抽象、同步/异步边界、监听清理、错误、变体、TypeScript facade 和 JSDoc 契约遵循 api-design。
- 调试奇怪构建失败前先检查 Nitro 当前最低要求:React Native 0.75+、Xcode 16.4+、Swift 5.9+、Android compileSdkVersion 34+、NDK 27+;Nitro Views 当前需要 React Native 0.78+ 和新架构。
- 每个原生 HybridObject 实现必须实现或继承该平台生成的 Hybrid*Spec;绝不要实现独立原生类并期望 Nitro 发现它。
- 原生实现类默认设为 final,除非确实需要继承;Swift 和 Kotlin HybridObject 尤其如此。
- 每个文件只保留一个顶层原生实现类型:Hybrid*Factory.swift/.kt 文件只含 Hybrid*Factory 类型,不含其他 struct/class/enum/interface/protocol;辅助会话、协调器、delegate、选项适配器和平台包装放各自文件。
- 不要把辅助类型嵌套在主类型内以避免拆文件;给辅助类型自己的具名文件。
- 把原生文件名当作作用域契约:以 HybridObject 命名的文件应聚焦该 HybridObject 实现,不收集扩展方法/属性、平台辅助、转换器、delegate、协议或可复用工具。
- 用行数作为原生文件审查信号:约 300 行以下通常可接受,但仅在满足一类型一文件和工厂内无辅助规则后;若大小来自可单独命名的辅助,拆分文件。
- 把原生转换辅助放在最小有意义的类型上:若转换只读一个生成 struct 或 enum,就放在该类型上,即使返回多个原生值;集合组合留在调用点,除非集合转换增加去重、验证、非空检查、批处理或错误聚合等真实行为。
- 绝不要把领域转换放在宽泛原生/通用接收者如 Kotlin Int 或 Swift Int 上;用领域类型的 companion、静态工厂或初始化方向,例如 BarcodeFormat.Companion.fromFormat(format: Int) 或 BarcodeFormat.from(format:)。
- 把每个 Kotlin 扩展 fun/val/var 和 Swift 扩展方法/属性放在单独的具名扩展/转换器文件中,尽量用 internal/package 可见性;Kotlin 转换器用 Type+operation.kt 命名如 BarcodeFormat+fromMLKitBarcodeFormat.kt;Hybrid* 文件或其他实现文件没有私有/微小/同文件例外。
- Hybrid*Factory.kt 或 Hybrid*.swift 文件不得包含条码格式映射、companion 扩展、平台转换工具或其他扩展。
- Hybrid*Factory 文件只做编排:解析生成选项、调用验证/预检辅助、创建/启动原生会话或平台 API、返回/reject Promise;不要在工厂文件定义会话/协调器/delegate 类、原生选项适配器、presenter 查找辅助、builder/config 适配器、条码映射、权限开关、Info.plist/manifest 检查或能力检查。
- 把原生转换拆成每个源/目标转换一个聚焦扩展文件,如 Barcode+toScannedCode.kt、TargetBarcodeFormat+toMLKitFormat.kt、BarcodeFormat+fromMLKitBarcodeFormat.kt;不要把简单 map { it.toX() } 藏在具体集合扩展如 Array<TargetBarcodeFormat>.toMLKitFormats() 后面。
- 把平台预检从 Hybrid* 工厂和实现方法中抽出:授权状态 switch、Info.plist/manifest 键验证、权限检查、硬件能力检查和服务可用性检查放聚焦辅助/扩展文件;HybridObject 调用点应读作短 guard 或一行 try/check。
- 不要用宽泛 Utils 文件替代内联预检;用小具名文件如 Bundle+CameraUsageDescription.swift、AVCaptureDevice+CameraAuthorization.swift 或聚焦的 Android 权限/能力辅助。
- Kotlin 中当平台暴露匹配注解或 @IntDef 时,注解 Android 平台 Int 常量,如 CameraX flash/capture/mirror mode 注解;按目标放在函数、返回值或参数上,选择语法前确认是 Java 还是 Kotlin 注解。
- 从 Android 注解 Int 转换时,在支持的情况下注解领域工厂/companion 函数的 format: Int 参数,比裸 Int 有更好的 IDE 和 lint 反馈。
- 尽早验证无效输入和必需的不支持行为;不要仅因当前原生后端忽略可选偏好就 reject 跨平台配置;若操作仍完成核心工作,忽略或降级偏好并通过能力或解析状态报告支持。
- 不要静默吞掉真实失败;请求结果无法交付时抛出、reject Promise 或通过显式错误回调/监听发出。
- 优先用能干净浮到 JS 的 Nitro/runtime 错误或语言原生异常;避免 Objective-C 风格 NSError 公开路径,除非生成 API 特别要求。
- 不要给 Nitro Modules 加空 iOS bridging header 如 Bridge.h;Nitro 生成所需 Swift/C++ 桥;仅当真实手写 Objective-C 代码需要时加 Objective-C/Objective-C++ header,并在 podspec 只包含必要文件。
- Android context 访问用 NitroModules.applicationContext 惰性获取,不可用时抛出清晰错误。
- Nitro 方法仅在快速、确定、本地工作时保持同步,如廉价对象创建、缓存元数据或纯转换;若实现复杂、重、随时间可能失败、已是原生异步或跨线程/进程边界,让 Nitro API 返回 Promise<T>。
- 用匹配平台线程模型的原生 Promise 辅助:Swift 中 DispatchQueue 拥有的工作(如 AVFoundation/session 队列)优先 Promise.parallel(queue),仅端到端包装 Swift async/await 或 Task API 时用 Promise.async;UIKit/VisionKit 主线程回调 API 优先手动 Promise 并在 Nitro 入口/回调边界直接 DispatchQueue.main.async,不要用 Task { @MainActor in ... } 作通用主线程跳转。
- 避免主/UI 线程,除非原生 API 要求;主线程段仅限 UI/展示/视图变更,解析、转换、I/O、会话协商和 CPU 工作放在自有队列/dispatcher/executor 或原生异步 API。
- 避免手动创建或传递 Promise<T> 实例;优先 Promise.async、Promise.parallel、Promise.resolved、Promise.rejected,因为它们通过结构化控制流恰好完成一次;仅桥接无法用辅助的原生完成/delegate/回调 API 时用手动 Promise,保持最小作用域,不传给任意辅助,保证每条路径恰好 resolve 或 reject 一次。
- 为回调/监听 API 写手动 Promise 前,先找或加聚焦扩展文件中的通用 suspend/async 适配器;Android 上把 Google Task<T> 一次包装为 Task+await.kt 用 suspendCancellableCoroutine,然后在 HybridObject 方法用 return Promise.async(scope) { task.await() }。
- 当可复用回调转 suspend 适配器能一次表达 API 时,不要在 Hybrid* 方法内手接 addOnSuccessListener/addOnFailureListener/addOnCanceledListener。
- 绝不要把线程跳转藏在生成的属性 getter 或 setter 后;若原生状态只能在特定队列/线程读写,暴露异步方法、监听/事件或显式生命周期操作。
- 避免一个操作内 Task、DispatchQueue、coroutine dispatcher、executor 和 JS/Nitro runtime 跳转链;为每个 HybridObject 或会话选一个原生所有者队列/线程/dispatcher,在 Promise、生命周期或回调边界跨入一次;重复跳转说明 HybridObject 边界或生命周期句柄错误。
- 绝不用 setTimeout、sleep、人为延迟、额外线程跳转或调用原生方法两次来修 Nitro 生命周期、就绪或竞态 bug;用 Promise、监听/事件、返回的已配置 HybridObject、显式状态转换或原生完成回调建模就绪;仅对外部硬件、OS 服务、远程服务或网络不确定性使用重试,且有界/可取消/幂等。
- 为拥有原生资源或大分配量的 HybridObject 实现 memorySize,以便 JS VM 在内存压力下回收。
- Nitro View 在视图拥有复用时需重置的状态时实现 prepareForRecycle;完整视图 spec、实现、注册和回收工作流见 spec-hybrid-view.md。
- 在一个库中混合 C++ HybridObject 与 Swift/Kotlin HybridObject:共享或热代码(如 OpenCV/帧处理/存储引擎)用 C++,平台服务、权限、文件路径、相机/会话 API 和 OS 集成用 Swift/Kotlin。
- C++ HybridObject 可接受 Swift/Kotlin 实现的 HybridObject 并调用其生成的 C++ spec API;例如 C++ StorageFactory 可接受 Swift/Kotlin PlatformContext 并通过生成的 C++ 接口调用 getTemporaryDirectory() 或 writeFile(...);C++ 只能访问公开 spec API,不能访问 Swift/Kotlin 私有字段。
- 除非已验证当前 Nitrogen 支持该方向,否则不要依赖 Swift/Kotlin 调用 C++ 实现的 HybridObject。
- 优先行为测试而非类型形状测试:Nitrogen 已在编译期强制 spec,测试应覆盖真实功能行为、输入、设置、失败路径和执行顺序。
- 可用时用 react-native-harness 在真实 React Native 环境做端到端测试;原生重的库对依赖硬件或 OS 行为的 API 面优先真机或 CI 设备农场覆盖。
- GitHub Actions 中对仅通过 React Native 公开的 API 优先 harness E2E 作业而非独立原生单元测试;仅当包有可在 React Native 外使用的原生目标或 RN harness 无法覆盖行为时,才加 Kotlin JUnit、XCTest 或其他原生专用测试。
- 开始前先确定用户意图:是创建新 Nitro Module 库,还是向现有库添加新 HybridObject。
- 创建新库时在任何命令前问全五个问题:库名、是否用 packages/ 的 monorepo(强烈推荐,默认是)、是否创建示例应用及位置(推荐,默认是;多个示例用 apps/example,小型单示例仓库用 example)、原生语言(iOS: swift 默认或 cpp;Android: kotlin 默认或 cpp;跨平台 C++ 仅用两者 cpp)、模块用途。
- 五个问题全部回答前不要越过构建序列第 1 步。
- 向现有库添加 HybridObject 时只问:HybridObject 名称、原生语言(iOS: swift 或 cpp?Android: kotlin 或 cpp?)、用途;然后直接跳到 spec-hybrid-object.md(写 spec)、spec-nitro-json.md(加 autolinking 条目)、native-nitrogen-codegen.md(重跑 nitrogen)和相关原生实现文件,跳过所有设置、monorepo 和示例应用步骤。
- 若新 HybridObject 是可渲染视图组件,用 spec-hybrid-view.md 而非普通 HybridObject spec 参考,它覆盖 HybridView spec、原生 view 实现、Android view manager 注册和 getHostComponent JS 接线。
- 典型构建序列:在单独分支工作,首个有用提交后开 draft PR;用 bunx nitrogen@latest init react-native-math 脚手架;写 spec 和 nitro.json 后在包目录运行 cd packages/react-native-math && bunx nitrogen;用 bunx @react-native-community/cli@latest init --skip-install MathExample 创建示例应用并 mkdir -p apps && mv MathExample apps/example(或 mv MathExample example);在 apps/example 运行 bun add ../../packages/react-native-math 和 bun add react-native-nitro-modules@<same-version-as-package>,然后 bun example android、bun example ios。
- 包入口如 src/index.ts、index.ts、index.js、index.tsx 必须保持 barrel:可含直接 re-export 和一行 Nitro 根导出如 export const camera = NitroModules.createHybridObject<Camera>('Camera'),但不含实际实现逻辑、函数、类、hooks、组件、分支、副作用或辅助定义;真实定义移到聚焦文件并 re-export。
- Nitro 包的 JS/TS 源码中保持缺失和变更显式:绝不用 void 0 代替 undefined,绝不用 ??=、||=、&&= 等逻辑赋值运算符;优先显式 if 块或直接赋值使回退行为可见。
- 最小 HybridObject spec 示例:import type { HybridObject } from 'react-native-nitro-modules'; export interface Math extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { add(a: number, b: number): number }。
- 最小运行时和类型导出示例:import { NitroModules } from 'react-native-nitro-modules'; import type { Math } from './specs/Math.nitro'; export const math = NitroModules.createHybridObject<Math>('Math'); export type { Math } from './specs/Math.nitro'。
- 最小 nitro.json 示例含 $schema、cxxNamespace、ios.iosModuleName、android.androidNamespace、android.androidCxxLibName 和 autolinking 块(每个 HybridObject 的 ios/android language 与 implementationClassName)。
- 根 package.json 脚本示例:scripts 中 specs 为 bun --cwd packages/react-native-math run specs,example 为 bun --cwd apps/example;运行 bun example android、bun example ios、bun specs。
- 按优先级参考:0 通用公开 API 形状(api-design)和 Nitro API 约束(本 SKILL.md);1 仓库结构与工作流;2 Nitrogen 脚手架;3 HybridObject spec 和 Hybrid View 组件;4 nitro.json autolinking;5 Nitrogen codegen;6 C++ 实现;7 Kotlin 实现;8 Swift 实现;9 示例应用设置;10 Android Gradle 路径;11 Metro + 安装 + 测试;12 npm 发布就绪;13 release-it 发布;14 VisionCamera 风格完整库模式。
- 按问题映射参考:设计公开 API 先用 api-design 再应用 Nitro 约束;需要最新通用 API 查官方文档、发布说明、源码仓库、包元数据或 llms-full.txt;推荐仓库结构用 main、强 README、packages/、apps/ 或 example/、可选 Fumadocs、scripts/、config/、.github/workflows/;不确定静态模块 vs 实例 API 时优先 HybridObject 承载原生状态;不知从何开始用 nitrogen init 脚手架;spec 语法错误修 *.nitro.ts interface;需要原生视图组件写 HybridView spec、实现 view、注册 Android manager、接线 getHostComponent;视图回调 prop 到达为 true 或从不触发时用 callback(...) 包装每个函数 prop(含 hybridRef);Android view manager not found 时在 createViewManagers 加生成的 Hybrid*ViewManager;autolinking 不工作时检查 nitro.json autolinking 块;Nitrogen 不生成文件时验证 spec 扩展名并从正确目录运行;C++ 类型不清时跟类型参考链接到规范示例;Kotlin 编译错误检查注解和 override 修饰符;Swift 编译错误检查类继承和属性签名;示例应用 Android 构建失败修 Gradle monorepo 路径;Metro 无法解析库时在 metro.config.js 加 watchFolders;示例与包版本不匹配时对齐各 workspace 的 react-native 版本;npm 包缺文件时修 package.json 的 files 字段;需要一键发布时在 bun release 后配置 release-it;需要完整功能库结构时用 VisionCamera 启发的包、API、hooks、视图和 Nitro 对象模型。
适用边界 · 注意事项
- 用户构建纯 JS 的 React 或 React Native 库,且任务不涉及 Nitro、HybridObjects、原生模块、codegen、C++/Swift/Kotlin 绑定或 react-native-nitro-modules 时,不要应用本技能。
- 创建仓库、重组布局、添加示例/文档/CI 或更改工作流策略时,仅在需要时加载 repo-structure-and-workflow.md,不要默认套用。
- 设置或审查 bun release / release-it 时才加载 release-it-publishing.md,不要在其他场景默认使用。