@flun/node-mobile-app
v4.1.2
Published
将 Node.js 项目一键打包为 Android / iOS 移动应用,运行时由 @flun/nodejs-mobile-react-native 提供;
Maintainers
Readme
@flun/node-mobile-app
简介
将 Node.js 项目一键打包为 Android / iOS 移动应用;运行时由 @flun/nodejs-mobile-react-native 提供,无需修改你的 Node 代码;
核心理念:你的 Node 项目保持不变,CLI 负责搬运,编译,打包;所有定制集中在 mobileAppConfig.js 一个文件;
特性
- 零侵入:Node 项目源码,依赖,配置都不动,CLI 在独立目录里完成构建
- 自动识别服务地址:拦截
server.js里的listen()调用,动态获取真实协议和端口 - 失败可见:5 秒内未启动,加载异常,App 显示错误页并支持展开详情(开发者可查)
- 增量构建:文件指纹判断缓存,无变化时 10 秒级重跑
- 跨平台:Android(Windows / macOS / Linux)+ iOS(仅 macOS)
- 离线可用:
build打出的 APK / IPA 内置 JS,无需连电脑也能跑 - node_modules 优化(默认开启):构建时先交叉编译原生模块,再自动执行六阶段优化--先清空 postinstall 资源(保留 js 供合并),
再合并 JS,清平台二进制,清开发文件,清中间产物,清冗余与空目录;编译源文件与 node-gyp 中间产物在编译完成后一并清理,仅保留
.node;大幅减少打包文件数与体积,加速启动,无需配置; - 热更新开箱可用:
test自动启动 Metro 并后台常驻,命令结束后仍可热更新;详见 自动化机制; - 签名切换自动处理:设备已装包与待装包签名不一致时,
test自动卸载重装,避免INSTALL_FAILED_UPDATE_INCOMPATIBLE;
包目录结构
@flun/node-mobile-app/
│
├── bin/
│ └── cli.js # CLI 入口(命令分发)
│
├── cli-assets/
│ └── server.js # 默认 server 模板(你的项目无 server.js 时复制过去)
│
├── scripts/
│ └── copy-files.js # postinstall 钩子:在你的项目根生成 mobileAppConfig.js
│
├── src/
│ ├── config.js # 配置加载 + 校验(DEFAULT_CONFIG / loadConfig / validateConfig)
│ ├── copy.js # 模板复制 / 你的项目复制 / ABI patch / extraRnDependencies 注入
│ ├── fingerprint.js # 缓存指纹(判断是否需要清理构建缓存)
│ ├── generate.js # 生成 main.js(桥接)+ mobileApp.runtime.ts(运行时配置)
│ ├── install.js # 依赖安装 + 原生模块编译 + 插件 abiFilters/SSL patch
│ ├── optimize-node-modules.js # node_modules 优化(六阶段:资源/JS合并/二进制/开发文件/中间产物)
│ ├── patcher.js # 应用信息 / Android / iOS 配置 patch
│ ├── platform-ios.js # iOS 构建(pod install / xcodebuild archive / export)
│ ├── utils.js # 通用工具(文件操作 / 进程调用 / adb / hermesc 定位)
│ └── commands/
│ ├── test.js # `test` 命令
│ ├── build.js # `build` 命令
│ └── clean.js # `clean` 命令
│
├── template/ # RN 工程模板(运行时复制到你的项目的 node-mobile-app-build/)
│ ├── android/ # Android 原生工程
│ ├── ios/ # iOS 原生工程
│ ├── App.tsx # RN 主界面(WebView + Node 桥接 + 错误页)
│ ├── index.js # RN 入口(注册根组件)
│ ├── app.json # RN 应用名(CLI 会覆盖)
│ ├── babel.config.js
│ ├── eslint.config.js
│ ├── metro.config.js # Metro 配置(watchFolders / nodeModulesPaths)
│ ├── mobileApp.runtime.d.ts # App.tsx 里 import 的类型声明(运行时由 CLI 生成 .ts)
│ ├── package.json # RN 工程依赖清单
│ ├── .prettierrc.js
│ ├── Gemfile
│ └── .watchmanconfig # watchman 文件监控配置(详见下方说明)
│
├── 非导出略...
├── 自定义libnode指南.md # 自编译 libnode.so 的完整流程
├── Android构建指南.md # Android 环境配置与踩坑清单
├── CHANGELOG.md # 更新日志
├── README.md # 本文件
├── LICENSE # 许可证
├── mobileAppConfig.js # 你的配置模板(postinstall 时复制到你的项目根)
└── package.json # 包配置关键约定:
template/是只读模板;CLI 每次test/build会把它复制到你的项目的node-mobile-app-build/, 你如手改模板内容会被覆盖- 你的持久化的自定义项应写进
mobileAppConfig.js,或通过keepFiles保留特定模板文件 template/.watchmanconfig会在copyTemplate时复制到node-mobile-app-build/.watchmanconfig,供 Metro 的 watchman 使用
.watchmanconfig 说明
位于包根目录,配置 watchman 的文件监控行为:
{
"ignore_dirs": [
".git", ".vs", ".vscode", ".idea",
"android/build", "android/app/build", "android/.gradle", "android/.cxx", "android/.kotlin",
"ios/build", "ios/Pods",
"node-mobile-app-build", "nodejs-assets",
"out", "dist", "dist-apk", "coverage"
],
"settle": 20
}ignore_dirs:排除的目录(构建产物,缓存,IDE 配置);不要排除node_modules,Metro 需要监控它来解析依赖settle:防抖延迟(毫秒),短时间内多次文件变更只触发一次重编- 改完后需
watchman watch-del-all && watchman shutdown-server才生效
安装前准备
| 项 | 要求 |
| ------------ | ---------------------------------------------------------------- |
| Node.js | 需满足 package.json 的 engines.node 字段 |
| Android 构建 | JDK 17,Android SDK(minSdk 29 / targetSdk 36 / compileSdk 37) |
| iOS 构建 | macOS + 完整 Xcode + CocoaPods |
| 可选 | watchman(提升 Metro 文件监听性能) |
| Windows 额外 | Visual Studio + Git for Windows(仅编译原生模块时需要,见下方说明) |
JDK 必须是 17,Android Gradle Plugin 目前不支持更高版本;
允许安装脚本执行
本包在安装时可能触发某些依赖包的自动脚本(如 postinstall 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了
ignore-scripts=true),可能会导致安装不完整或运行时异常;
推荐在项目根目录的 package.json 中添加 allowScripts 字段,显式放行本包及其依赖的脚本:
{
"allowScripts": {
"esbuild": true,
"@flun/node-mobile-app": true,
"@flun/nodejs-mobile-react-native":true
}
}安装
npm i -D @flun/node-mobile-app安装完成后,会在你的项目根目录自动生成 mobileAppConfig.js(配置文件模板);
命令行
快速开始
# 1. 编辑配置:打开 mobileAppConfig.js,改 appId / appName / serverPath 等
# 2. 开发调试(需 adb 可连的设备,走 Metro 热更新)
npx node-mobile-app test
# 3. 打包发布
npx node-mobile-app build --release命令
npx node-mobile-app test- 编译并安装到设备(需 adb 可连的设备,真机或模拟器;默认架构 arm64-v8a);自动启动 Metro 并后台常驻, 支持热更新;设备已装包签名不一致时自动卸载
npx node-mobile-app test --ios- iOS 版(仅 macOS)
npx node-mobile-app build- 打 debug APK(离线可用)
npx node-mobile-app build --release- 打 release APK(Android 与 iOS 均生效);未配置
android.signing时用模板自带 debug keystore 签名, 可安装但不可上架;配置后改用正式签名
- 打 release APK(Android 与 iOS 均生效);未配置
npx node-mobile-app build --ios- 打 IPA(仅 macOS)
npx node-mobile-app clean- 清理构建缓存
npx node-mobile-app example- 重新生成
mobileAppConfig.js;配置文件被误删时使用,已存在则跳过
- 重新生成
常用选项
| 选项 | 用途 |
| --------------- | ----------------------------------------------- |
| --ios | 目标平台为 iOS(默认 Android) |
| --release | 打 release 包(仅 build,Android 与 iOS 均生效) |
| --device <id> | 指定设备(test) |
| --arch <abi> | 指定 ABI,逗号分隔(build,仅 Android) |
| --debug | 显示详细日志(优化各阶段明细,各命令完整输出) |
安装 APK 到手机
build 打出的 APK 在 outputDir(默认 ./out/)里;手动装到手机:
Windows(用环境变量定位 adb,任何用户通用):
$adb = "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe"
& $adb install -r "<编译好的 APK 完整路径>"macOS / Linux:
adb install -r "<编译好的 APK 完整路径>"例如(build 末尾会打印完整路径):
# Windows
& $adb install -r "D:\你的项目\out\myapp-1.0.0-release.apk"# macOS / Linux
adb install -r "./out/myapp-1.0.0-release.apk"说明:
-r= 覆盖安装(保留应用数据)- 签名不同的包不能互相覆盖:
build的 release 包用android.signing的 keystore 签名,test/build的 debug 包用模板自带 debug keystore 签名;两者包名相同但签名不同,直接install -r会报INSTALL_FAILED_UPDATE_INCOMPATIBLE: signatures do not match;需先执行adb uninstall <appId>再安装(会清除应用数据) adb找不到?在mobileAppConfig.js里配android.adbPathtest命令已自动处理签名冲突并安装,无需手动
参考配置
所有定制集中在这一个文件;不配置也能跑(用默认值);
必填
appName- 类型:
string \| null - 默认:
null - 说明:应用显示名称(默认从
package.json的name读)
- 类型:
outputDir- 类型:
string \| null - 默认:
null - 说明:APK / IPA 输出目录(默认
./out)
- 类型:
showLoading- 类型:
boolean - 默认:
true - 说明:是否显示加载指示器
- 类型:
loadingText- 类型:
string - 默认:
'正在启动服务...' - 说明:加载提示文字
- 类型:
enableJavaScript- 类型:
boolean - 默认:
true - 说明:是否启用 JavaScript
- 类型:
enableDomStorage- 类型:
boolean - 默认:
true - 说明:是否启用 DOM Storage
- 类型:
allowFileAccess- 类型:
boolean - 默认:
true - 说明:是否允许访问本地文件
- 类型:
mixedContentMode- 类型:
string - 默认:
'always' - 说明:混合内容模式:
never/always/compatibility
- 类型:
backgroundColor- 类型:见下
- 默认:
'#ffffff' - 说明:WebView 背景色;支持:固定色
'#ffffff'/ 跟随系统'system'/ 深浅两值{ light, dark }
allowSelfSignedCert- 类型:
boolean - 默认:
false - 说明:见下文"HTTPS 自签证书"
- 类型:
Android
sdkPath- 类型:
string \| null - 默认:
null - 说明:Android SDK 路径(默认自动查找)
- 类型:
adbPath- 类型:
string \| null - 默认:
null - 说明:adb 路径(默认自动查找)
- 类型:
minSdkVersion- 类型:
int - 默认:
29 - 说明:最低 SDK(29=Android 10.0)
- 类型:
targetSdkVersion- 类型:
int - 默认:
36 - 说明:目标 SDK
- 类型:
compileSdkVersion- 类型:
int - 默认:
37 - 说明:编译 SDK
- 类型:
buildToolsVersion- 类型:
string - 默认:
'37.0.0' - 说明:构建工具版本
- 类型:
abiFilters- 类型:
string[] - 默认:
['arm64-v8a'] - 说明:打包架构(真机
arm64-v8a,模拟器加x86_64)
- 类型:
icon- 类型:
string - 默认:
'./build/icon.png' - 说明:应用图标(建议 512×512 PNG)
- 类型:
usesCleartextTraffic- 类型:
boolean - 默认:
true - 说明:是否允许明文 HTTP
- 类型:
permissions- 类型:
string[] - 默认:
[] - 说明:额外权限(如
'android.permission.CAMERA')
- 类型:
signing- 类型:
object - 默认:-
- 说明:自定义签名配置.不填则用模板自带的 debug keystore 签名(可安装测试,不可上架);填则以下 4 项均为必填
- 类型:
signing.keystore- 类型:
string - 默认:-
- 说明:keystore 路径(相对你的项目根)
- 类型:
signing.storePassword- 类型:
string - 默认:-
- 说明:keystore 密码
- 类型:
signing.keyAlias- 类型:
string - 默认:-
- 说明:密钥别名
- 类型:
signing.keyPassword- 类型:
string - 默认:-
- 说明:密钥密码(通常与
storePassword相同)
- 类型:
生成自签名 keystore
如需自定义签名(上架或长期使用),先生成 keystore 文件.keytool 随 JDK 提供:
- 若已设
JAVA_HOME:& "$env:JAVA_HOME\bin\keytool.exe" - 否则用 JDK 安装目录下的完整路径,例如
C:\Program Files\Java\jdk-17.0.20\bin\keytool.exe
# Windows PowerShell
& "<JDK 安装目录>\bin\keytool.exe" -genkeypair -v `
-keystore "<你的项目根>\build\release.keystore" `
-alias testkey `
-keyalg RSA -keysize 2048 -validity 10000 `
-storepass <keystore 密码> -keypass <密钥密码> `
-dname "CN=Test, OU=Dev, O=Test, L=City, S=State, C=CN"# macOS / Linux
keytool -genkeypair -v \
-keystore "<你的项目根>/build/release.keystore" \
-alias testkey \
-keyalg RSA -keysize 2048 -validity 10000 \
-storepass <keystore 密码> -keypass <密钥密码> \
-dname "CN=Test, OU=Dev, O=Test, L=City, S=State, C=CN"生成后,把路径,密码,别名填入 mobileAppConfig.js 的 android.signing:
signing: {
keystore: './build/release.keystore', // 相对你的项目根
storePassword: '<keystore 密码>',
keyAlias: 'testkey',
keyPassword: '<密钥密码>',
},提示:
storepass/keypass可填相同值,对应配置里storePassword/keyPassword也填相同值.四项缺一不可, 缺项时构建前会报错.
iOS
| 字段 | 类型 | 默认 | 说明 |
| ----------------------------- | -------- | -------------------- | ---------------------------------------------------- |
| deploymentTarget | string | '15.1' | 最低 iOS 版本 |
| icon | string | './build/icon.png' | 应用图标(建议 1024×1024 PNG) |
| signing.teamId | string | - | Apple Developer Team ID |
| signing.provisioningProfile | string | - | 描述文件路径(写入 exportOptions.plist) |
| signing.certificate | string | - | P12 证书路径(导入临时 keychain 供 xcodebuild 签名用) |
| signing.certificatePassword | string | - | P12 密码 |
高级
| 字段 | 类型 | 默认 | 说明 |
| ---------------------------- | --------- | ------ | -------------------------- |
| advanced.keepAlive | boolean | true | 是否发送心跳保持 Node 进程 |
| advanced.heartbeatInterval | int | 5000 | 心跳间隔(毫秒) |
扩展点
| 字段 | 类型 | 默认 | 说明 |
| --------------------- | ---------- | ---- | --------------------------------------------------- |
| keepFiles | string[] | [] | 不重置的模板文件(路径相对 node-mobile-app-build/) |
| extraRnDependencies | string[] | [] | 额外 RN 原生依赖包名(需先安装在你的开发字段中) |
排除 / 白名单
| 字段 | 说明 |
| -------------- | -------------------------------------------------------------------------- |
| excludeFiles | 复制到 build 目录时跳过的文件/目录(支持 *.log,build/ 等) |
| allowScripts | 移动端 Node 环境里允许跑安装脚本的包(会写入 nodejs-project/package.json) |
node_modules 优化
构建时自动优化 node_modules(默认开启,无需配置),减少打包文件数与体积,加速启动.
| 字段 | 类型 | 默认 | 说明 |
| --------- | ---------- | ------ | --------------------------------------------------------- |
| enabled | boolean | true | 是否启用优化 |
| exclude | string[] | [] | 排除优化时异常或不想优化的包(写 package.json 的 name) |
编译与优化顺序:先交叉编译原生模块(见 install.js),再执行以下六阶段优化:
- 清空资源:扫描各包
postinstall引用的目录/文件,保留目录结构,清空内容;js 文件一律保留,留待合并阶段处理; - 合并 JS:按包入口 bundle,多个 JS 文件合并为单文件(
bundled/目录);合并后同步删除package.json的scripts字段(运行时无安装期,且 Gradlenpm rebuild会触发脚本导致失败); - 清平台二进制:按目标平台 +
abiFilters判定;prebuilds/<platform>-<arch>按目录整体处理--Android 只保留android,linux/darwin/win32删除;iOS 只保留darwin;其余二进制逐文件按文件头判定; - 清开发文件与编译源文件:删除
.md/.map/.d.ts/.h/.c/.cc/.cpp/.cxx等非运行文件; 原生模块已在优化前完成编译,编译源文件无需保留; - 清 node-gyp 中间产物:删除
build/Release/obj.target/下的中间副本,以及.o/.d/.mk/.stamp/Makefile等编译中间文件; - 清不参与运行的文件和空目录.
缓存:快照记录在 node-mobile-app-build/.deps-snapshot.json,构建时自动判断"重装并优化 / 仅优化 / 跳过";
改动模板或依赖会自动触发重编.
异常排查:
optimize: { enabled: false }整体关闭优化;optimize: { exclude: ['包名'] }排除优化时异常或不想优化的包;- 加
--debug查看优化各阶段明细与各命令完整输出.
自动化机制
- 快照双哈希:
.deps-snapshot.json记录optimizeHash(optimize-node-modules.js内容哈希)与installerHash(install.js内容哈希);任一变化即触发重装、重编、重优化,避免流程代码更新后仍复用旧缓存; - bundle 变化失效 Gradle 缓存:
test生成 JS bundle 后,与android/app/build/intermediates/assets/debug/mergeDebugAssets/中的合并结果比对 SHA256;不一致时删除合并目录,强制 Gradle 重新合并;一致时保持 UP-TO-DATE; - 编译前执行
patch-package.js:把原生模块package.json中的node-gyp-build替换为node-gyp-build-mobile, 使编译走@flun/nodejs-mobile-gyp而非 npm 自带的node-gyp; - Metro 生命周期:
test执行前检测localhost:8081/status;未运行则以 detached 方式启动react-native start(日志写入node-mobile-app-build/.metro.log),就绪后继续;run-android加--no-packager,命令结束后 Metro 保持运行;30 秒未就绪打印警告并回落到 APK 内置 bundle; - 输出分级:步骤标题 0 空格,动作与状态 2 空格,结果与完成 4 空格,子项详情 8 空格;优化阶段作为步骤子项,阶段标题 4 空格、 阶段结果 6 空格;
- 精简与详细双模式:默认只输出关键行与结果,
--debug或OPTIMIZE_DEBUG=1时全量输出(含 Gradle 与 Metro 完整日志);
服务地址说明
CLI 自动识别 server.js 实际监听的协议和端口--拦截 net.Server.prototype.listen,从 server.address() 取真实端口,从
Server 类型判断 http / https;
因此 mobileAppConfig.js 不需要配置端口/协议;动态端口(listen(0))会拿到系统分配的真实端口;
失败场景:
server.js加载抛异常 → App 显示"应用启动失败"- 5 秒内未调用
listen()→ App 显示"应用启动失败"
你会看到简要提示,开发者点开"▶ 查看详细信息"能看到具体原因;
HTTPS 自签证书
webview.allowSelfSignedCert 控制是否放行不受信任的 HTTPS 证书:
| 场景 | 是否受开关影响 |
| ------------------------------------------- | ------------------- |
| HTTP | 无关 |
| HTTPS + 真证书(Let's Encrypt 等) | 无关 |
| HTTPS + 自签证书 + 已装进设备信任库 | 无关 |
| HTTPS + 自签证书 + 未装设备(本地测试最常见) | 必须设为 true |
实现方式:
- Android:patch
RNCWebViewClient.java,仅对127.0.0.1/localhost放行 - iOS:patch
RNCWebViewImpl.m,同上
其他域名仍严格校验;生产环境建议关闭;
Android / iOS 差异
| 项 | Android | iOS |
| ------------ | --------------------------------------------------------- | ---------------------------------------- |
| 构建环境 | Windows / macOS / Linux | 仅 macOS |
| 运行时版本 | 官方提供多个版本,可用 NODE_MOBILE_PREBUILT_VERSION 切换 | 当前为 v18.20.4,更高版本后续逐步适配 |
| Express 版本 | 产物带 full-icu,4.x / 5.x 均可用 | 当前产物不带 full-icu,必须用 4.x |
| full-icu | 自 v22.23.2 产物起支持 | 暂无 |
| test 离线 | 有(内置 bundle) | 有(需 FORCE_BUNDLING=1) |
| 图标尺寸 | 5 个密度(48~192) | 9 个尺寸(40~1024) |
iOS 运行时版本:iOS 附件的实际运行时版本随适配进度变化,当前对应 v18.20.4;如需更高版本,可克隆 nodejs-mobile
仓库 自行编译 NodeMobile.xcframework,流程与 Android 自编译 libnode.so
类似(见 自定义libnode指南.md);
你的项目结构
运行 CLI 后,你的项目根目录的样子:
你的项目/
├── package.json # 你自己的
├── mobileAppConfig.js # CLI 生成的配置(可编辑)
├── server.js # Node 启动脚本(你写的,或 CLI 生成的示例)
├── node_modules/ # 你的依赖
├── build/ # 你的资源(图标,keystore 等)
│ ├── icon.png
│ └── release.keystore
│── 你的其它文件/目录...
└── node-mobile-app-build/ # CLI 生成的 RN 工程(可整体删除重建)
├── android/ ios/ App.tsx index.js ...
├── mobileApp.runtime.ts # CLI 生成,App.tsx 读
└── nodejs-assets/
└── nodejs-project/ # 你的 Node 项目副本
├── server.js # 你的源码
├── main.js # CLI 生成,桥接代码
│── 你允许的其它生产文件/目录...
├── package.json # 只保留 dependencies
└── node_modules/ # 你的生产依赖相关文档
- Android构建指南.md -- Android 环境配置,踩坑清单
- 自定义libnode指南.md -- 自编译
libnode.so的完整流程
常见问题
Q:test 拔掉 USB 后 App 加载慢?
test 打的是 debug 版,启动时优先连 Metro;CLI 会在 [9/11] 自动启动 Metro 并保持后台常驻,但设备通过 adb reverse 访问
localhost:8081,拔掉 USB 后端口转发失效,设备连不上 Metro,debug 包等待超时后才回落到 APK 内置的 bundle;这个"等超时"是
RN debug 版的固有行为;
拔线后的启动速度(从快到慢):
npx node-mobile-app build --release打包后安装到手机--最快(release 版不连 Metro)npx node-mobile-app build打包后安装到手机--次之(debug 版,会先尝试连 Metro 再回退)npx node-mobile-app test后手动打开应用--最慢(同上,且test面向"连 USB 热更新"场景)
若需要热更新,保持 USB 连接(维持 adb reverse 的端口转发);不需要热更新时,用 build --release 打包可避免等待 Metro
超时.
Q:test 装到手机后拔 USB 就崩?
前提是 APK 里有内置 bundle;test 每次都会先 react-native bundle 打进
APK;如果崩,检查 CLI 输出里 [7/11] 生成 JS bundle(离线可用) 是否成功;
Q:Node 进程启动失败怎么排查?
App 显示"应用启动失败"页 → 点"▶ 查看详细信息" → 看到具体原因(如 加载 ./server.js 失败 -- ...);
进一步用 adb logcat 看 Node 日志:
adb logcat -c
adb shell am force-stop <你的 appId>
adb shell am start -n <你的 appId>/.MainActivity
adb logcat | grep -E "NODEJS-MOBILE|ReactNativeJS"Q:npm install 时提示 install-scripts not yet covered by allowScripts?
这是 npm 的提醒,表示某些依赖带有 postinstall / install 脚本,尚未在 allowScripts 里显式放行;@flun/node-mobile-app
及其依赖(@flun/nodejs-mobile-react-native、esbuild 等)需要执行这些脚本才能完整安装;
放行方式见 允许安装脚本执行:在项目根 package.json 中添加 allowScripts 字段,逐项设为 true;
未放行的后果:依赖虽能装完,但脚本未执行,运行时可能报模块缺失或二进制找不到;
Q:Android 构建卡在拉取依赖(react-android / hermes-android 等)?
Android 模板的 settings.gradle 与 build.gradle 已在 google() / mavenCentral() 之前插入阿里云镜像,
国内网络优先走镜像;海外网络镜像不命中会自动回退官方源,不影响功能.
若仍卡住:
- 检查镜像可达:浏览器打开
https://maven.aliyun.com/repository/public是否正常; - 检查本机
~/.gradle/init.gradle是否有与仓库顺序冲突的配置(如强制替换仓库),有则临时移除以排除干扰; - 停掉残留 Daemon 后重试:在
node-mobile-app-build/android/下执行gradlew --stop,再重跑test/build.
Q:Express 版本怎么选?
- Android:内置完整 ICU(full-icu)→ 可以用 Express 5
- iOS:暂无 ICU → 必须用 Express 4
Express 5 的路由语法与 4 不同(如 app.get('*') 要改成 app.get('/{*splat}')),迁移时留意;
Q:libnode.so 和 NodeMobile.xcframework 是怎么来的?
从 @flun/nodejs-mobile-react-native 1.0.1 起,两套预编译二进制不在 npm 包里,改为 postinstall 阶段自动从 Gitee /
GitHub Release 下载:
| 资源 | 目标位置 |
| ------------------------------ | --------------------------------------------------------------------------- |
| android-libnode.zip | node_modules/@flun/nodejs-mobile-react-native/android/libnode/ |
| ios-nodemobile.zip(仅 macOS) | node_modules/@flun/nodejs-mobile-react-native/ios/NodeMobile.xcframework/ |
- 默认下载版本以
@flun/nodejs-mobile-react-native的package.json中prebuiltAssets.version字段为准 - Gitee 优先,失败自动回退 GitHub
- 已装版本匹配则跳过,不重复下
- 全部源失败时打印下载链接,不中断 npm 安装
环境变量:
NODE_MOBILE_PREBUILT_VERSION=<版本号> # 覆盖默认版本(须为该包 Release 中实际存在的 tag)
NODE_MOBILE_PREBUILT_SKIP=1 # 跳过全部下载
NODE_MOBILE_PREBUILT_IOS_SKIP=1 # 只跳过 iOSQ:如何替换成自定义编译的 libnode.so?
如果你自编译了 libnode.so(如需要特定 Node 版本或自定义 ICU),可手动覆盖自动下载的产物:
- 把编译好的
libnode.so覆盖到android/libnode/bin/<架构>/(只放你实际编译的架构) - 如果
mobileAppConfig.js的android.abiFilters与 libnode 支持的架构不一致,同步调整 - 跑
npx node-mobile-app build或npx node-mobile-app test
CLI 会自动处理缓存:指纹机制检测到 libnode.so 或 abiFilters 变化,自动清理 .cxx / android/build / app/build,
全量重编;你无需手动清任何缓存;
详细流程见 自定义libnode指南.md;
Q:iOS 提示 fallbackSourceURL does not override?
RN 0.87 的 API 若变动,AppDelegate.swift 里的 fallback 可能不生效--不影响"连 Mac + Metro"正常开发,只是拔 Mac 后无法
fallback 到内置 bundle;届时反馈,我们更新适配;
许可证
ISC
