# @flun/node-mobile-app 中文文档

规范地址：https://npmcn.com/package/%40flun/node-mobile-app/

版本：4\.2\.4
npm 元数据更新时间：2026-11-14T01:32:58.135Z
中文译文更新时间：2026-11-15T02:34:08.764Z
软件包许可：ISC

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40flun/node-mobile-app
原文对照：https://npmcn.com/package/%40flun/node-mobile-app/?lang=original
项目主页：https://github.com/OpenFlun/node-mobile-app#readme
源码仓库：https://github.com/OpenFlun/node-mobile-app

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：419 kB，71 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- Node.js 要求：\>=22\.12\.0
- 运行依赖：23 个
- 首次发布：2026-09-20T03:53:44.896Z
- 最新版本发布：2026-11-14T01:32:57.960Z
- 近 30 天下载量：3736（2026-10-15 至 2026-11-13，npm 官方下载统计）
- 已发布版本数：40（近 12 个月 40 个）

## 中文简介

(原始内容存档于2018-10-12) (英语). Node.js QQAndroid / iOS QQ, @flun/nodejs-mobile-react-inative 97; ;

## 安装

```sh
npm install @flun/node-mobile-app
```

也可以使用：`pnpm add @flun/node-mobile-app`、`yarn add @flun/node-mobile-app`、`bun add @flun/node-mobile-app`

## 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 秒级重跑
-   **内置镜像探测 + 可自定义**:Gradle 发行版默认走腾讯云,未配置时按腾讯、阿里、官方顺序探测;Maven 仓库默认 阿里云 public/google/gradle-plugin 加官方源,两者均可在 `mobileAppConfig.js` 里自定义;
-   **跨平台**:Android(Windows / macOS / Linux)+ iOS(仅 macOS)
-   **离线可用**:`build` 打出的 APK / IPA 内置 JS,无需连电脑也能跑
-   **node\_modules 优化(默认开启)**:构建时先交叉编译原生模块,再自动执行六阶段优化--先清空 postinstall 资源(保留 js 供合并), 再合并 JS,清平台二进制,清开发文件,清中间产物,清冗余与空目录;编译源文件与 node-gyp 中间产物在编译完成后一并清理,仅保留 `.node`;大幅减少打包文件数与体积,加速启动,无需配置;
-   **热更新开箱可用**:`test` 自动启动 Metro 并后台常驻,命令结束后仍可热更新;详见 [自动化机制](https://npmcn.com/package/%40flun/node-mobile-app/#readme-%E8%87%AA%E5%8A%A8%E5%8C%96%E6%9C%BA%E5%88%B6);
-   **签名切换自动处理**:设备已装包与待装包签名不一致时,`test` 自动卸载重装,避免 `INSTALL_FAILED_UPDATE_INCOMPATIBLE`;

* * *

## 包目录结构

```txt
@flun/node-mobile-app/
│
├── bin/
│   └── cli.js                    # CLI 入口(命令分发)
│
├── cli-assets/
│   └── server.js                 # 默认 server 模板(你的项目无 server.js 时复制过去)
│
├── scripts/
│   ├── copy-files.js             # postinstall 钩子:在你的项目根生成 mobileAppConfig.js
│   └── generate-autolinking.js   # 生成 autolinking 配置,供 settings.gradle 调用
│
├── 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 的文件监控行为:

```json
{
  "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 目前不支持更高版本; **`engines.node` 只约束开发机**(驱动构建的 Node),与移动端运行时版本无关;Android / iOS 的运行时由 `@flun/nodejs-mobile-react-native` 的 `prebuiltAssets.version` 决定,当前支持 Node.js v18,不受开发机 Node 版本限制;

### 允许安装脚本执行

本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了 `ignore-scripts=true`),可能会导致安装不完整或运行时异常;

推荐在项目根目录的 `package.json` 中添加 `allowScripts` 字段,显式放行本包及其依赖的脚本:

```json
{
  "allowScripts": {
    "esbuild": true,
    "@flun/node-mobile-app": true,
    "@flun/nodejs-mobile-react-native":true
  }
}
```

* * *

## 安装

```bash
npm i -D @flun/node-mobile-app
```

## 安装完成后,会在你的项目根目录自动生成 `mobileAppConfig.js`(配置文件模板);

## 命令行

### 快速开始

```bash
# 1. 编辑配置:打开 mobileAppConfig.js,改必填项 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 签名, 可安装但不可上架;配置后改用正式签名
-   `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,任何用户通用):

```powershell
$adb = "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe"
& $adb install -r "<编译好的 APK 完整路径>"
```

macOS / Linux:

```bash
adb install -r "<编译好的 APK 完整路径>"
```

例如(`build` 末尾会打印完整路径):

```powershell
# Windows
& $adb install -r "D:\你的项目\out\myapp-1.0.0-release.apk"
```

```bash
# 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.adbPath`
-   `test` 命令已自动处理签名冲突并安装,无需手动

## 参考配置

所有定制集中在这一个文件;`serverPath` 字段值是必填项;

### 入口与标识

-   `serverPath`
    -   类型:`string`
    -   默认:`'./server.js'`
    -   说明:Node.js 启动脚本路径,指向的文件必须存在
-   `appId`
    -   类型:`string`
    -   默认:`'com.example.app'`
    -   说明:应用唯一标识(反向域名格式,Android/iOS 通用)
-   `versionCode`
    -   类型:`int`
    -   默认:`1`
    -   说明:内部版本号,必须为正整数

构建前会校验以下项,不满足即报错:用户项目根必须有 `package.json`;`appId` 需为反向域名格式; `versionCode` 需为正整数;`android.abiFilters` 不能为空;`android.gradleDistributionUrl` 与 `android.mavenRepositories` 若配置,格式不合法也会报错(见 Android 章节).

### 应用信息

-   `appName`
    -   类型:`string \| null`
    -   默认:`null`
    -   说明:应用显示名称(默认从 `package.json` 的 `name` 读)
-   `outputDir`
    -   类型:`string \| null`
    -   默认:`null`
    -   说明:APK / IPA 输出目录(默认 `./out`)
-   `versionName`
    -   类型:`string`
    -   默认:`'1.0.0'`
    -   说明:用户可见版本名(Android versionName / iOS CFBundleShortVersionString)

### WebView

以下字段均位于 `webview` 对象下:

-   `webview.showLoading`
    -   类型:`boolean`
    -   默认:`true`
    -   说明:是否显示加载指示器
-   `webview.loadingText`
    -   类型:`string`
    -   默认:`'正在启动服务...'`
    -   说明:加载提示文字
-   `webview.enableJavaScript`
    -   类型:`boolean`
    -   默认:`true`
    -   说明:是否启用 JavaScript
-   `webview.enableDomStorage`
    -   类型:`boolean`
    -   默认:`true`
    -   说明:是否启用 DOM Storage
-   `webview.allowFileAccess`
    -   类型:`boolean`
    -   默认:`true`
    -   说明:是否允许访问本地文件
-   `webview.mixedContentMode`
    -   类型:`string`
    -   默认:`'always'`
    -   说明:混合内容模式:`never` / `always` / `compatibility`
-   `webview.backgroundColor`
    -   类型:见下
    -   默认:`'#ffffff'`
    -   说明:WebView 背景色;支持:固定色 `'#ffffff'` / 跟随系统 `'system'` / 深浅两值 `{ light, dark }`
-   `webview.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'`
    -   说明:构建工具版本
-   `gradleDistributionUrl`
    -   类型:`string \| null`
    -   默认:`null`
    -   说明:Gradle 发行版的完整下载地址.未配置时按腾讯、阿里、官方顺序探测,三者均不可达则回落官方并警告; 已配置时直接使用,失败不回退,并提示可改回 `null` 或换可达地址
-   `mavenRepositories`
    -   类型:`string[] \| null`
    -   默认:`null`
    -   说明:自定义 Maven 仓库数组,同时替换 `settings.gradle` 与根 `build.gradle` 的仓库列表;未配置时保持模板默认 (阿里云 `public`/`google`/`gradle-plugin` + 官方 `google`/`mavenCentral`)
-   `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`

```powershell
# 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"
```

```bash
# 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`:

```js
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`),再执行以下六阶段优化:

1.  **清空资源**:扫描各包 `postinstall` 引用的目录/文件,保留目录结构,清空内容;js 文件一律保留,留待合并阶段处理;
2.  **合并 JS**:按包入口 bundle,多个 JS 文件合并为单文件(`bundled/` 目录);合并后同步删除 `package.json` 的 `scripts` 字段(运行时无安装期,且 Gradle `npm rebuild` 会触发脚本导致失败);
3.  **清平台二进制**:按目标平台 + `abiFilters` 判定;`prebuilds/<platform>-<arch>` 按目录整体处理--Android 只保留 `android`, `linux` / `darwin` / `win32` 删除;iOS 只保留 `darwin`;其余二进制逐文件按文件头判定;
4.  **清开发文件与编译源文件**:删除 `.md` / `.map` / `.d.ts` / `.h` / `.c` / `.cc` / `.cpp` / `.cxx` 等非运行文件; 原生模块已在优化前完成编译,编译源文件无需保留;
5.  **清 node-gyp 中间产物**:删除 `build/Release/obj.target/` 下的中间副本,以及 `.o` / `.d` / `.mk` / `.stamp` / `Makefile` 等编译中间文件;
6.  **清不参与运行的文件和空目录**.

**缓存**:快照记录在 `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 **注意**:`engines.node` 约束的是开发机;移动端运行时可以是 Node.js v18; 仓库](https://github.com/OpenFlun/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](https://npmcn.com/package/%40flun/node-mobile-app/Android%E6%9E%84%E5%BB%BA%E6%8C%87%E5%8D%97.md) -- Android 环境配置,踩坑清单
-   [自定义libnode指南.md](https://npmcn.com/package/%40flun/node-mobile-app/%E8%87%AA%E5%AE%9A%E4%B9%89libnode%E6%8C%87%E5%8D%97.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 版的固有行为;

拔线后的启动速度(从快到慢):

1.  **`npx node-mobile-app build --release`** 打包后安装到手机--最快(release 版不连 Metro)
2.  **`npx node-mobile-app build`** 打包后安装到手机--次之(debug 版,会先尝试连 Metro 再回退)
3.  **`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 日志:

```bash
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` 等)需要执行这些脚本才能完整安装;

放行方式见 [允许安装脚本执行](https://npmcn.com/package/%40flun/node-mobile-app/#readme-%E5%85%81%E8%AE%B8%E5%AE%89%E8%A3%85%E8%84%9A%E6%9C%AC%E6%89%A7%E8%A1%8C):在项目根 `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` 是否有与仓库顺序冲突的配置(如强制替换仓库),有则临时移除以排除干扰;
-   若所在网络对默认镜像均不友好,可在 `mobileAppConfig.js` 里配置 `android.mavenRepositories` 指定自己的仓库数组, 配置后会完全替换模板默认仓库,未配置则保持模板默认;
-   停掉残留 Daemon 后重试:在 `node-mobile-app-build/android/` 下执行 `gradlew --stop`,再重跑 `test` / `build`.

### Q:Android 构建卡在下载 Gradle 发行版(`gradle-*-bin.zip`)?

Gradle Wrapper 下载发行版走单个 `distributionUrl`,没有多 URL 自动回退机制.默认模板指向腾讯云镜像, 中国网络可直接使用.若卡住:

-   直接配置完整地址:`android.gradleDistributionUrl` 填一个可达的 `gradle-*-bin.zip` 地址; 配置后不再探测,失败不回退,会提示改回 `null` 或换可达地址;
-   交回自动探测:把 `android.gradleDistributionUrl` 设为 `null`,工具会按腾讯、阿里、官方顺序探测, 三者均不可达时回落官方并警告;
-   Wrapper 只对单个 URL 做重试,不会在多个地址间切换;若地址本身不可达,换地址或改回 `null` 走探测才是有效手段;
-   已下载完成的部分会留在 `~/.gradle/wrapper/dists/` 下,下次构建复用,不会重复下载.

### 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 安装**

环境变量:

```bash
NODE_MOBILE_PREBUILT_VERSION=<版本号>     # 覆盖默认版本(须为该包 Release 中实际存在的 tag)
NODE_MOBILE_PREBUILT_SKIP=1              # 跳过全部下载
NODE_MOBILE_PREBUILT_IOS_SKIP=1          # 只跳过 iOS
```

### Q:如何替换成自定义编译的 `libnode.so`?

如果你自编译了 `libnode.so`(如需要特定 Node 版本或自定义 ICU),可手动覆盖自动下载的产物:

1.  把编译好的 `libnode.so` 覆盖到 `android/libnode/bin/<架构>/`(**只放你实际编译的架构**)
2.  如果 `mobileAppConfig.js` 的 `android.abiFilters` 与 libnode 支持的架构不一致,同步调整
3.  跑 `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

* * *

## 贡献

-   源码:[GitHub](https://github.com/OpenFlun/node-mobile-app)
-   中国镜像:[gitee](https://gitee.com/OpenFlun/node-mobile-app)
-   问题反馈:[Issues](https://github.com/OpenFlun/node-mobile-app/issues)
-   主页:[npm 包页面](https://www.npmjs.com/package/@flun/node-mobile-app#readme)

## 常见问题

### @flun/node-mobile-app 是什么？

\(原始内容存档于2018-10-12\) \(英语\)\. Node\.js QQAndroid / iOS QQ, @flun/nodejs-mobile-react-inative 97; ;

### 如何安装 @flun/node-mobile-app？

在项目目录运行 npm install @flun/node-mobile-app，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 node-mobile-app，可以用 npx node-mobile-app 直接运行。

### @flun/node-mobile-app 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/flun\_\_node-mobile-app 是否存在。

### @flun/node-mobile-app 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @flun/node-mobile-app 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.12\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @flun/node-mobile-app 可以商用吗？

@flun/node-mobile-app 使用 ISC 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @flun/node-mobile-app 的下载量有多少？

根据 npm 官方下载统计，@flun/node-mobile-app 在 2026/10/15 – 2026/11/13 这 30 天内被下载 3,736 次，日均约 125 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @flun/node-mobile-app 还在维护吗？

最新版本 4\.2\.4 发布于 2026/11/14（1 天前）；至今共发布 40 个版本，其中过去 12 个月发布了 40 个；首次发布于 2026/09/20。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

