# nodejs-mobile-react-native **Repository Path**: OpenFlun/nodejs-mobile-react-native ## Basic Information - **Project Name**: nodejs-mobile-react-native - **Description**: 为 React Native 应用提供完整的 Node.js。让 JS 服务端代码和 React Native 前端共享同一进程,双向通信。 - **Primary Language**: Unknown - **License**: ISC - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-23 - **Last Updated**: 2026-11-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # @flun/nodejs-mobile-react-native ## 简介 为 React Native 应用提供完整的 Node.js; 让 Node.js 服务端代码与 React Native 前端运行于同一进程的不同线程, 双向通信; 基于 [nodejs-mobile-react-native](https://github.com/nodejs-mobile/nodejs-mobile-react-native) 的 flun fork; Android 与 iOS 各自内置独立的 Node.js 运行时(版本对照见"安装"章节); 可直接用于任何 React Native 项目, 也可由 [`@flun/node-mobile-app`](https://www.npmjs.com/package/@flun/node-mobile-app) 在构建时自动调用; - GitHub: https://github.com/OpenFlun/nodejs-mobile-react-native - Gitee: https://gitee.com/OpenFlun/nodejs-mobile-react-native - npm: https://www.npmjs.com/package/@flun/nodejs-mobile-react-native ### 与原版的差异 #### rn-bridge 的处理 `rn-bridge` 是 native binding 的 JS 包装, 必须走 CJS; 因本包是 `type: module`, 入口文件改名为 `index.cjs`,`package.json` 的 `main` 指向它.无论引用方是 CJS 还是 ESM, 都能正常加载; #### 预编译资源下载(libnode.so / NodeMobile.xcframework) 两套预编译二进制体积较大, 不塞进 npm 包, 改为 **postinstall 阶段自动下载**并解压到对应目录: | 资源 | 用途 | | --------------------- | --------------------------------------------------------- | | `android-libnode.zip` | Android 的 `libnode.so`(具体架构以 `prebuiltAssets` 为准) | | `ios-nodemobile.zip` | iOS 的 `NodeMobile.xcframework` | - 下载源:Gitee 优先, 失败自动回退 GitHub - 各 asset 独立处理, 已装版本匹配则跳过(不重复下载) - 全部下载失败时打印下载链接,**不会中断 npm 安装**, 可手动下载覆盖到对应目录 **npm 包体积**:tarball 不含预编译二进制,装包很快,二进制随后拉取; > 需要覆盖版本 / 跳过下载等进阶操作, 见 [安装](#安装) 章节的"首次安装的预编译资源下载"; #### 代码风格 | 项 | 原版 | 本 fork | | ------------------- | ---------------------------------- | ------------------------------------------------- | | 模块系统 | CommonJS | **ESM**(`package.json` 的 `type: "module"`) | | 导出方式 | 文件内 `module.exports` | 文件末尾统一 `export { ... }` | | 函数声明 | `function xxx` | `const xxx = (...) => {}`(`class` 保留) | | 注释 | 英文 | 中文 | | 依赖 | `nodejs-mobile-gyp` | **`@flun/nodejs-mobile-gyp`**(去停更依赖) | | `xcode` 依赖 | 保留(RN 0.60 autolinking 前的遗留) | **删除**(代码中零引用) | | `react-native` peer | 强制 | **`peerDependenciesMeta.optional`**(避免重复安装) | --- ## 特性 - Node.js 运行时内置 Android 与 iOS, 与 React Native 前端同进程不同线程, 支持双向通信 - 预编译二进制不经 npm 包分发, 由 postinstall 自动下载(Gitee 优先, 失败回退 GitHub), 包体小、装包快 - 二进制版本匹配时跳过下载; 全部下载失败时打印链接, 不中断 npm 安装 - ESM / CJS 双兼容: `rn-bridge` 走 CJS 入口 `index.cjs`, 引用方无论 CJS 或 ESM 均可加载 - 可作为中间层被 `@flun/node-mobile-app` 自动调用, 也可直接用于任意 React Native 项目 --- ## 包目录结构 ```txt @flun/nodejs-mobile-react-native/ │ ├── android/ # Android 原生工程 │ ├── build.gradle # gyp 路径探测,abiFilters,NDK 工具链注入,Windows 适配(cmd / sh.exe / 正斜杠) │ ├── CMakeLists.txt # 编译 rn-bridge.cpp + native-lib.cpp │ └── src/main/ │ ├── AndroidManifest.xml │ ├── cpp/ │ │ ├── native-lib.cpp # JNI 入口, 设置 NODE_PATH, 启动 Node 引擎, stdout/stderr 重定向 │ │ ├── rn-bridge.cpp # 注册 native binding(NODE_MODULE_LINKED)与 channel 队列 │ │ └── rn-bridge.h # 头文件 │ └── java/com/janeasystems/rn_nodejs_mobile/ │ ├── RNNodeJsMobileModule.java # RN 原生模块:start / sendMessage / registerChannel 等 │ └── RNNodeJsMobilePackage.java │ ├── install/ # postinstall 复制到宿主项目的运行时资源 │ └── resources/ │ ├── nodejs-assets/nodejs-project/ │ │ ├── sample-main.js # 用户 main.js 示例(ESM) │ │ └── sample-package.json # 用户 package.json 示例(含 type: module) │ └── nodejs-modules/builtin_modules/rn-bridge/ │ ├── index.cjs # rn-bridge 运行时模块(CJS,.cjs 强制后缀以兼容 ESM 项目) │ └── package.json # main: index.cjs │ ├── ios/ # iOS 原生工程 │ ├── NodeRunner.hpp # Node 引擎生命周期管理 │ ├── NodeRunner.mm # 设置 NODE_PATH, 启动 Node, stdout/stderr 回调 │ ├── rn-bridge.cpp # iOS 端同名 native binding │ ├── rn-bridge.h # 头文件 │ ├── RNNodeJsMobile.h │ ├── RNNodeJsMobile.m # RN 原生模块;-r 加载 dlopen override │ ├── RNNodeJsMobile.xcodeproj/ │ ├── RNNodeJsMobile.xcworkspace/ │ ├── libnode/include/node/ # Node 头文件(随 npm 包分发) │ └── NodeMobile.xcframework/ # iOS 静态库; 由 postinstall 下载(见简介中的"预编译资源下载") │ ├── scripts/ # 构建与安装期脚本(全部 ESM) │ ├── create-node-structure.js # postinstall: 复制 nodejs-assets 到宿主项目根; │ │ 宿主项目依赖 @flun/node-mobile-app 时跳过 │ ├── download-prebuilt-assets.js # postinstall: 从 Gitee / GitHub Release 下载 libnode.so / NodeMobile.xcframework │ ├── patch-package.js # 补丁原生模块 package.json(node-gyp-build → node-gyp-build-mobile) │ ├── ios-copy-nodejs-project.sh # iOS Script Phase: 复制 Node 项目 │ ├── ios-build-native-modules.sh # iOS Script Phase: 调用 @flun/nodejs-mobile-gyp 编译原生模块 │ ├── ios-sign-native-modules.sh # iOS Script Phase: 签名原生模块 │ ├── ios-remove-framework-simulator-strips.sh # iOS Script Phase: 移除模拟器切片 │ ├── ios-create-plists-and-dlopen-override.js # 生成 framework Info.plist 与 dlopen 覆盖数据 │ ├── override-dlopen-paths-preload.js # 运行时 preload, 覆盖 process.dlopen 指向 App 内 Frameworks │ └── plisttemplate.xml ├── CHANGELOG.md # 版本变更记录 ├── index.d.ts # TypeScript 类型声明 ├── index.js # JS 桥接层入口 ├── LICENSE ├── native.js # 实际桥接层:start / startWithArgs / startWithScript / channel ├── nodejs-mobile-react-native.podspec # iOS Pod 配置(vendored NodeMobile.xcframework) ├── package.json # type: module; postinstall 触发结构生成与预编译资源下载 ├── react-native.config.js # RN CLI 配置: 指定 Android sourceDir 与 iOS 的 4 个 Script Phase ├── README.md # 本文件 └── types.d.ts # 共享类型声明(ChannelCallback, StartupOptions) ``` ## 安装前准备 ### 允许安装脚本执行 本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等); 如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了 `ignore-scripts=true`), 可能会导致安装不完整或运行时异常; 推荐在项目根目录的 `package.json` 中添加 `allowScripts` 字段, 显式放行本包及其依赖的脚本: ```json { "allowScripts": { "@flun/nodejs-mobile-gyp":true, "@flun/nodejs-mobile-react-native": true // 如果依赖的其它包(如 bcrypt, electron-winstaller 等)也有脚本, 请按需添加, 格式相同 } } ``` --- ### 系统与配置要求 本包是 [`@flun/node-mobile-app`](https://www.npmjs.com/package/@flun/node-mobile-app) 与 [`@flun/nodejs-mobile-gyp`](https://www.npmjs.com/package/@flun/nodejs-mobile-gyp) 之间的中间层; 完整的宿主平台与工具链要求见 gyp 的"[支持的系统与配置要求](https://github.com/OpenFlun/nodejs-mobile-gyp#支持的系统与配置要求)".本包相关的要点: | 目标平台 | 宿主平台 | 额外要求 | | -------- | ----------------------- | ----------------------------------------------------------- | | Android | Windows / macOS / Linux | Android NDK;Windows 上还需 Visual Studio 与 Git for Windows | | iOS | **仅 macOS** | 完整 Xcode(Command Line Tools 单独不够)+ CocoaPods | - **Node.js**:最低版本见 `package.json` 的 `engines.node` 字段 - **Python 3.6+**:gyp 构建时需要 - **首次安装的预编译资源**:Android 与 iOS 的运行时二进制由 postinstall 自动下载,无需手动准备 --- ## 安装 ```bash npm i @flun/nodejs-mobile-react-native ``` iOS 需运行 `pod install` 链接原生代码.通过 `@flun/node-mobile-app` 构建时 CLI 会自动执行,直接使用本包时需手动运行: ```bash cd ios && pod install ``` 如果你的依赖是 `@flun/node-mobile-app`, 那么你不需要手动安装本包, 它由 CLI 在构建时自动调用; ### 首次安装的预编译资源下载 本包的 npm tarball 仅含代码(不含预编译二进制),安装后 postinstall 会自动从 Gitee / GitHub Release 拉取: - **Android**:`android-libnode.zip` → 覆盖到 `android/libnode/` - **iOS**(仅 macOS):`ios-nodemobile.zip` → 覆盖到 `ios/NodeMobile.xcframework/` 细节见简介中的"预编译资源下载"; 环境变量(按需设置): ```bash # 覆盖当前版本(具体版本号请在 package.json 的 "prebuiltAssets" 查看) # 具体下载版本由 package.json 的 "prebuiltAssets.version" 决定;同一 tag 下 Android 与 iOS 附件的实际运行时版本可能不同 NODE_MOBILE_PREBUILT_VERSION=你的目标版本号 # 跳过全部下载 NODE_MOBILE_PREBUILT_SKIP=1 # 只跳过 iOS(macOS 上只做 Android 编译时用) NODE_MOBILE_PREBUILT_IOS_SKIP=1 ``` ### Node.js 运行时能力对照 Android 与 iOS 采用**各自独立**的 Node.js 运行时;下载哪个 Release tag 由 `package.json` 的 `prebuiltAssets.version` 决定(可用环境变量 `NODE_MOBILE_PREBUILT_VERSION` 覆盖), 但同一 tag 下 **Android 与 iOS 附件的实际运行时版本可能不同**--以最终下载到的产物为准: | 平台 | ICU | Express | 备注 | | ------- | -------------- | ---------------- | --------------------------- | | Android | 完整(full-icu) | 4.x / 5.x 均支持 | 支持 `\p{...}` Unicode 正则 | | iOS | 暂无 ICU | 仅 4.x | 不支持 `\p{...}` | **关于 full-icu**: - 完整 ICU(full-icu)自 nodejs-mobile 基于 Node.js v22.23.2 起支持; 更低基线版本不支持 `\p{...}` 等 Unicode 属性转义 - **iOS 目前尚未适配 full-icu**, 后续会逐步跟进, 请耐心等待 - 如果你自行编译适配了 iOS 上的 full-icu, 欢迎分享:open@flun.top > 两个平台的运行时**彼此独立**; 表中状态为**当前发布情况**. ## 函数方式 按运行位置分两层,各层入口如下: | 入口 | 运行位置 | 用途 | | ----------- | --------------- | ------------------------------------ | | `nodejs` | React Native 侧 | 启动 Node 线程、收发消息 | | `rn_bridge` | Node 侧 | 与 React Native 通信、取应用数据目录 | 详细接口见以下 API 三节; ### React Native 层 API 从 React Native 代码直接调用: ```js import * as nodejs from '@flun/nodejs-mobile-react-native'; ``` 可用方法: - `nodejs.start` - `nodejs.startWithArgs` - `nodejs.startWithScript` - `nodejs.channel.addListener` - `nodejs.channel.removeListener` - `nodejs.channel.post` - `nodejs.channel.send` > `nodejs.channel.send(...msg)` 等价于 `nodejs.channel.post('message', ...msg)`, 保留是为了向后兼容; > `nodejs.channel` 继承自 [React Native 的 `EventEmitter` 类][rn-ee], 去掉 `emit`, 增加 `post` 和 `send`; #### nodejs.start(scriptFileName [, options]) | 参数 | 类型 | | -------------- | ---------------- | | scriptFileName | `string` | | options | `StartupOptions` | 从 `nodejs-project` 目录下的某个文件启动 nodejs-mobile 运行时线程; #### nodejs.startWithArgs(command [, options]) | 参数 | 类型 | | ------- | ---------------- | | command | `string` | | options | `StartupOptions` | 从 `nodejs-project` 目录下的某个文件启动, 并把额外参数传给它;`command` 通常包含脚本文件名作为第一个参数, 例如: ``` command = 'main.js --insecure-http-parser --zero-fill-buffers' ``` #### nodejs.startWithScript(scriptBody [, options]) | 参数 | 类型 | | ---------- | ---------------- | | scriptBody | `string` | | options | `StartupOptions` | 用一个脚本字符串启动 nodejs-mobile 运行时线程; #### nodejs.channel.addListener(event, callback) | 参数 | 类型 | | -------- | ---------- | | event | `string` | | callback | `function` | 为 Node 侧发出的自定义事件注册回调; #### nodejs.channel.removeListener(event, callback) | 参数 | 类型 | | -------- | ---------- | | event | `string` | | callback | `function` | 移除之前通过 `addListener` 注册的回调.`callback` 必须与注册时传入的是同一个函数引用. #### nodejs.channel.post(event, ...message) | 参数 | 类型 | | ---------- | ---------------------------------------------------------------- | | event | `string` | | ...message | 任何可用 `JSON.stringify` 序列化,`JSON.parse` 反序列化的 JS 类型 | 在 Node 侧触发一个自定义事件; #### nodejs.channel.send(...message) | 参数 | 类型 | | ---------- | ---------------------------------------------------------------- | | ...message | 任何可用 `JSON.stringify` 序列化,`JSON.parse` 反序列化的 JS 类型 | 在 Node 侧触发一个 `'message'` 事件; 是 `nodejs.channel.post('message', ...message)` 的别名; #### StartupOptions: `object` | 名称 | 类型 | 默认值 | 说明 | | ---------------------- | --------- | ------ | --------------------------------------------------------------------- | | redirectOutputToLogcat | `boolean` | `true` | 是否把 Node 的 stdout/stderr 重定向到 Android logcat(仅 Android 有效) | ### Node 层 API(rn-bridge) 从 Node 代码通过 `rn-bridge` 模块调用: ```js import { createRequire } from 'module'; const require = createRequire(import.meta.url); const rn_bridge = require('rn-bridge'); ``` 可用方法: - `rn_bridge.channel.on` - `rn_bridge.channel.removeListener` - `rn_bridge.channel.post` - `rn_bridge.channel.send` - `rn_bridge.app.on` - `rn_bridge.app.datadir` > `rn_bridge.channel.send(...msg)` 等价于 `rn_bridge.channel.post('message', ...msg)`, 保留是为了向后兼容; > `rn_bridge.channel` 继承自 [Node 的 `EventEmitter` 类][node-ee], 去掉 `emit`, 增加 `post` 和 `send`; #### rn_bridge.channel.on(event, callback) | 参数 | 类型 | | -------- | ---------- | | event | `string` | | callback | `function` | 为 React Native 侧发出的自定义事件注册回调; > 接收 `nodejs.channel.send` 的消息, 用: > ```js > rn_bridge.channel.on('message', listenerCallback) > ``` #### rn_bridge.channel.removeListener(event, callback) | 参数 | 类型 | | -------- | ---------- | | event | `string` | | callback | `function` | 移除之前通过 `on` 注册的回调.`callback` 必须与注册时传入的是同一个函数引用. #### rn_bridge.channel.post(event, ...message) | 参数 | 类型 | | ---------- | ---------------------------------------------------------------- | | event | `string` | | ...message | 任何可用 `JSON.stringify` 序列化,`JSON.parse` 反序列化的 JS 类型 | 在 React Native 侧触发一个自定义事件; #### rn_bridge.channel.send(...message) | 参数 | 类型 | | ---------- | ---------------------------------------------------------------- | | ...message | 任何可用 `JSON.stringify` 序列化,`JSON.parse` 反序列化的 JS 类型 | 在 React Native 侧触发一个 `'message'` 事件; 是 `rn_bridge.channel.post('message', ...message)` 的别名; #### rn_bridge.app.on(event, callback) | 参数 | 类型 | | -------- | ---------- | | event | `string` | | callback | `function` | 注册 App 事件回调; 当前支持 `'pause'` 和 `'resume'`, 分别在应用进入后台/前台时自动触发; ```js rn_bridge.app.on('pause', (pauseLock) => { console.log('[node] app paused.') pauseLock.release() }) rn_bridge.app.on('resume', () => { console.log('[node] app resumed.') }) ``` `'pause'` 事件在应用切到后台时触发; iOS 上系统会等待所有 `'pause'` 处理器返回后才真正挂起应用; 为了让 iOS 知道何时可以安全挂起, 每个 `'pause'` 监听器会收到一个 `pauseLock` 参数, 调用它的 `release()` 表示该监听器已经完成工作; 所有锁都释放后(或 iOS 强制挂起), 应用才会进入挂起状态; ```js rn_bridge.app.on('pause', (pauseLock) => { server.close(() => { // 服务器停止监听, 当前连接全部关闭后, 应用才会挂起; pauseLock.release() }) }) ``` > **警告**:iOS 上应用最终一定会被挂起;`pause` 事件里应尽快完成清理, 并确保每个监听器都调用 `pauseLock.release()`, 否则应用会在 iOS 允许的范围内一直占用后台; #### rn_bridge.app.datadir() 返回一个可写的持久化数据目录; iOS 上是 `NSDocumentDirectory`, Android 上是 `FilesDir`; #### Channel 回调:`function(arg)` | 名称 | 类型 | | ---- | ---------------------------------------------------------------- | | arg | 任何可用 `JSON.stringify` 序列化,`JSON.parse` 反序列化的 JS 类型 | 通道内部用 `JSON.stringify` 序列化,`JSON.parse` 反序列化; 这意味着: - JS 的 `Date` 会被转成字符串 - 函数会从对象里被剔除 - 只支持 `Boolean`,`Number`,`String`,`Object`,`Array`,`null` 这些类型(符合 [JSON 数据交换语法标准](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)) - `NaN`,`Infinity`,`-Infinity` 会被 `JSON.stringify` 转成 `null` - `undefined` 在对象里会被剔除, 在数组里会被转成 `null` - 循环引用会抛出 `TypeError` ### 其他 Node API 说明 #### os.tmpdir() - **iOS**:`os.tmpdir()` 返回一个临时目录, 因为 iOS 会把应用的 `TMPDIR` 环境变量设为 `NSTemporaryDirectory` 的等价路径; - **Android**:Android 系统没有为应用定义临时目录, 插件会把 `TMPDIR` 环境变量设为应用上下文 `CacheDir` 的值; ### 集成示例 #### Node.js 项目 `@flun/nodejs-mobile-react-native` 安装后, 会在你的应用里生成 `nodejs-assets/nodejs-project/` 目录; 这个目录会被打进 APK/IPA, Node 引擎启动时会从这里加载 `main.js`; 目录里包含两个示例文件: - `sample-main.js` - `sample-package.json` 建议把它们重命名为 `main.js` 和 `package.json` 后开始开发; > **注意**:`sample-main.js` 和 `sample-package.json` 会在安装/更新 `@flun/nodejs-mobile-react-native` 时被覆盖, 不要直接改动它们; `main.js` 示例内容(ESM + `createRequire`): ```js import { createRequire } from 'module'; const require = createRequire(import.meta.url); const rn_bridge = require('rn-bridge'); // 把收到的每条 react-native 消息回显; rn_bridge.channel.on('message', (msg) => { rn_bridge.channel.send(msg); }); // 通知 react-native:Node 已初始化; rn_bridge.channel.send('Node was initialized.'); ``` > **注意**:`package.json` 默认是 `type: module`,`main.js` 本身是 ESM, 不能直接写 `require('rn-bridge')`; 如需加载 `rn-bridge`, 请用 `createRequire`: > > ```js > import { createRequire } from 'module'; > const require = createRequire(import.meta.url); > const rn_bridge = require('rn-bridge'); > ``` React Native 的打包器(Metro)会扫描项目目录下的 JS 文件; 为避免它把 `nodejs-assets/nodejs-project/` 里的服务端 JS 也当作应用代码解析, 请在 `metro.config.js` 里用 `blockList` 排除该目录, 配置示例见下方"故障排查"章节; Node.js 运行时通过 Unix 风格的路径访问文件; Android 上, Node 项目会在应用首次启动(或更新后)从 APK 的 assets 复制到默认应用数据目录下的 `nodejs-project/`; > **注意**: 应用每次更新后项目目录会被覆盖, 不要把它当作持久化存储; 持久化请用 `rn_bridge.app.datadir()`; 为了加速资源解压(避免递归遍历 assets 目录), 编译时会把文件清单 `file.list` 和目录清单 `dir.list` 一起打进 assets; 这在 Android 6.x 及更旧版本上能规避 assets manager 的一个严重性能 bug; #### Node 模块 在 `nodejs-assets/nodejs-project/` 里跑 `npm install` 即可添加 Node 模块(前提是已有 `package.json`); #### 原生模块 Windows / Linux / macOS 上均支持编译 **Android** 原生模块;编译 **iOS** 原生模块则必须在 macOS 上进行; 插件会扫描 `nodejs-project` 目录下的 `.gyp` 文件, 自动识别原生模块; **前置工具链**: - **Android**:需要 Android NDK; 插件会自动定位 NDK 并把工具链路径注入构建环境, 无需手动设置 `ANDROID_NDK_HOME`; - **iOS**:需要**完整 Xcode**(仅 macOS;Command Line Tools 单独不够); - **Windows 额外需要**:Visual Studio(含"使用 C++ 的桌面开发"工作负载)与 Git for Windows(提供 make 所需的 POSIX 工具); 完整清单见 [@flun/nodejs-mobile-gyp 的系统要求](https://github.com/OpenFlun/nodejs-mobile-gyp#支持的系统与配置要求); **运行时版本与开发机的 Node 版本相互独立**: `prebuiltAssets.version` 指定的是**下载用的 Release tag**, 与开发机安装的 Node 版本无关;**同一 tag 下 Android 与 iOS 附件的实际运行时版本可能不同**(以产物为准);只要开发机的 Node 满足 `package.json` 的 `engines.node` 要求即可编译原生模块--产物的 ABI 由 NDK 与运行时自带的头文件决定, 开发机的 Node 仅用于驱动构建工具链; 切换运行时版本请设置环境变量 `NODE_MOBILE_PREBUILT_VERSION`(详见"安装"章节); Android 上编译原生模块耗时较长(需为每个目标架构分别编译);编译产物 `.node` 会按架构分开打进应用, 运行时选择正确的那个; 如果想手动控制开关, 可以创建 `nodejs-assets/BUILD_NATIVE_MODULES.txt`: - 内容 `1`: 强制启用编译 - 内容 `0`: 强制关闭编译 例如: ```sh echo "1" > nodejs-assets/BUILD_NATIVE_MODULES.txt react-native run-android ``` ##### Prebuilds(预编译产物) 插件会自动检测预编译的 `.node` 文件并**关闭该模块的即时编译**; 识别路径: ``` nodejs-assets/nodejs-project/node_modules//prebuilds/-/.node ``` 支持的组合: - PLATFORM = `android` - ARCH = `arm` - ARCH = `arm64` - ARCH = `x64` - PLATFORM = `ios` - ARCH = `arm64` - ARCH = `x64` 检测到 prebuild 后, 插件会删除该模块的 `binding.gyp` 并修改 `package.json`, 让 node-gyp 忽略它; 如果你是原生模块作者, 想为自己的模块提供 nodejs-mobile 的 prebuild, 参考 CLI 工具 [prebuild-for-nodejs-mobile](https://github.com/staltz/prebuild-for-nodejs-mobile); #### React Native 应用 先从 React Native 侧导入: ```js import * as nodejs from '@flun/nodejs-mobile-react-native'; ``` 在应用主组件的生命周期里启动 Node: ```js nodejs.start('main.js') nodejs.channel.addListener( 'message', msg => alert('From node: ' + msg) ) ``` 这会告诉原生代码启动一个专用线程, 从 `nodejs-assets/nodejs-project/main.js` 开始运行 Node, 并在收到 Node 消息时弹窗显示; > **注意**:Node 项目以单例形式运行在独立线程上, 只有第一次 `nodejs.start()` 生效, 后续调用不会启动新线程; 这意味着 react-native 的热重载不会影响 Node 项目, 需要重启应用; 发送消息给 Node: ```js