@exodus/react-native-bundle-loader

中文文档
0.2.0-exodus.6发布于 2026/11/02MITTypeScriptESM + CJS零依赖近 30 天下载 1.1万

装入远程反应原生JS捆绑.

中文使用文档
文档目录13 节
  1. 威胁模式
  2. 安装
  3. 使用量
  4. 验证装载( 建议)
  5. 平台支持
  6. 如何装入
  7. Android 集成系统
  8. 1. 牵引线
  9. 2. 登记一揽子计划
  10. 3. 将钩捆装入反大气层
  11. 证明
  12. 警卫
  13. 许可证

@ exodus/ react-native-bundle-loader (英语)

装入远程 React 原生 JS 捆绑, 可选 hash 定位完整性核查 在桥重装之前.

这是Exodus安全坚固的叉子 react-native-bundle-loader (原始内容存档于Jusbrasil;上游GitHub repo已删除;见 来源 详细情况请查阅.

威胁模式

装入远程JS捆绑是通过构建,远程代码执行在主机应用程序内部. 该图书馆仅供内部/开发建筑使用,没有带外、静态地悬挂的地物旗,不得将其运入储存的建筑. 见 SECURITY.md。 。 。 .

这个 loadVerified() API关闭了主导运行时间风险:它以本地方式下载捆绑,用平台加密方式将字节(iOS: CommonCrypto CC_SHA256,Android: (中文(简体) ) MessageDigest SHA-256),将散列比作恒定时间内的呼叫器提供文摘,而后再从app-private存储中加载已验证的字节. 任何在取回和再装入之间突变的响应都会被拒绝 .

安装

yarn add @exodus/react-native-bundle-loader

iOS: (英语)

cd ios && pod install

Android:既需要Gradle接线,也需要主机应用程序的更改——参见 Android 集成系统 见下文.

使用量

import BundleLoader from '@exodus/react-native-bundle-loader';

await BundleLoader.loadVerified(
  'https://bundles.example.com/main.jsbundle',
  // Lower-case hex sha256, exactly 64 chars
  '4f1b9c…ec'
);

行为:

  • URL必须使用 https: 计划.
  • 所期望的sha256必须是64个特征的六角弦.
  • 下载,SHA-256散列,以及恒时相比较,都发生在本地代码中. 这样可以避开赫耳墨斯河 RangeError 联署材料方面 response.arrayBuffer() 大型捆绑上的原因(~70 MB).
  • 匹配时,字节被写入到app-私密存储并加载捆绑(见下文平台注释).
  • 在不匹配时, 会出现出错, 而当前捆绑则未受影响 .
  • 远程捆绑活动 仅一届. 下个寒冷开始返回本地捆绑——匹配消费者从开发者预览工具中期待的行为.

工作在iOS和Android上.

图书馆曝光 仅限 已验证路径。 没有未经证实的 load() API: 装入没有本地 SHA-256 验证的远程捆绑是一个 未认证的远程代码执行原始,因此被去除.

平台支持

能力iOS系统机器人
loadVerified(url, sha256)✅✅
runningMode()✅✅

如何装入

iOS系统 下载和通过本地方式验证捆绑 NSURLSession + 键 CommonCrypto CC_SHA256,然后将已核实的字节写入 NSTemporaryDirectory() 与 NSDataWritingAtomic | NSDataWritingFileProtectionComplete (核查前无一文). 它存储文件 URL 在 NSUserDefaults 下级 RNBundleLoaderPendingURLKey 和电话 [bridge reload]; 主机应用程序 loadSourceForBridge: 读取待定的 URL 并从中加载, 所以桥本身的 bundleURL ——因此 SourceCode.scriptURL ——从不突变,保持资产分辨率正确. 这是一次在程序内重载;在ARC下,旧桥(及其赫尔墨斯跑道时间)在新桥分配前被放出,所以没有双模峰.

机器人 使用进程重启,而不是进程内桥接互换。 原因:Android的ART垃圾收集器是非决定性的. 当新 React 上下文和已有上下文一起创建时, ART 并不能保证旧的 Hermes runtime 的本土堆积在新运行时间分配前被释放出. 在真实世界捆绑大小(~50 MB of Hermes bytecode)上,这会导致OOM. 重新启动的过程通过确保只运行一次就能完全避免问题.

在下载和散列验证后,模块:

  1. 下载到 a 每个呼叫唯一的临时文件 (单位:千美元)File.createTempFile 内 Context.getCacheDir()),验证SHA-256,然后 解剖促进 给 Context.getCacheDir()/verified-bundle.jsbundle ——犬行道从不持有未经验证或部分字节. 每个调用(而不是共享的固定路径)的独特临时,意味着两个相重叠的负载永远无法在验证和重命名之间互换对方的文件. 在散列不匹配或下载出错时,临时文件被删除,而当前的捆绑则未受影响.
  2. 设置一发旗帜 SharedPreferences (单位:千美元)"BundleLoader" 编号 : "pending_remote_bundle"),使用同步键 commit() 所以旗帜能活过即将到来的过程.
  3. 通过 startActivity + 键 Process.killProcess。 。 。 .

在下一次发布时,主机应用读取了旗子,禁用地铁(所以) ReactInstanceManager 不查询打包器而忽略文件——通过RN 0.78的字节代码分析确认有必要,并服务 verified-bundle.jsbundle 作为联署材料,供本届会议使用。 旗被先用后再重新启动,然后返回地铁.

Android 集成系统

因为Android需要主机应用程序的修改而不能被封入模块本身,所以需要以下的人工步骤.

1. 牵引线

settings.gradle 有条件地包括次级项目(单元是: devDependency; prod CI 运行 yarn install --production 而目录将不存在 :

def bundleLoaderDir = new File(rootProject.projectDir, '../node_modules/@exodus/react-native-bundle-loader/android')
if (bundleLoaderDir.exists()) {
    include ':@exodus_react-native-bundle-loader'
    project(':@exodus_react-native-bundle-loader').projectDir = bundleLoaderDir
}

app/build.gradle ——仅依赖调试构建 :

if (new File("$rootDir/../node_modules/@exodus/react-native-bundle-loader/android").exists()) {
    debugImplementation project(':@exodus_react-native-bundle-loader')
}

2. 登记一揽子计划

单位 MainApplication.java内部 getPackages(),通过反射添加软件包,这样缺失的模块(在 prod CI 中缺失)不会导致编译时间错误:

if (BuildConfig.DEBUG) {
    // devDependency absent in prod CI (yarn install --production); reflection avoids a compile-time import
    try {
        packages.add((ReactPackage) Class.forName("com.reactnativebundleloader.BundleLoaderPackage")
                .getDeclaredConstructor().newInstance());
    } catch (ReflectiveOperationException e) {
        throw new RuntimeException(e);
    }
}

3. 将钩捆装入反大气层

将这三种方法添加到您的 ReactNativeHost 匿名子类 MainApplication.java数字 :

import java.io.File;

// ...

@Override
public boolean getUseDeveloperSupport() {
    if (BuildConfig.DEBUG && hasPendingRemoteBundle()) {
        // Must disable dev support: when enabled and Metro is reachable,
        // ReactInstanceManager queries the packager and ignores getJSBundleFile().
        return false;
    }
    // No pending bundle — clear the active flag so runningMode() returns LOCAL.
    getSharedPreferences("BundleLoader", MODE_PRIVATE)
            .edit().remove("active_remote_bundle").apply();
    return BuildConfig.DEBUG;
}

@Override
protected String getJSBundleFile() {
    if (BuildConfig.DEBUG && hasPendingRemoteBundle()) {
        File cachedBundle = new File(getCacheDir(), "verified-bundle.jsbundle");
        if (cachedBundle.exists()) {
            // Consume the one-shot latch: next restart goes back to Metro.
            getSharedPreferences("BundleLoader", MODE_PRIVATE).edit()
                    .remove("pending_remote_bundle")
                    .putBoolean("active_remote_bundle", true)
                    .apply();
            return cachedBundle.getAbsolutePath();
        }
    }
    return null;
}

private boolean hasPendingRemoteBundle() {
    return getSharedPreferences("BundleLoader", MODE_PRIVATE)
            .getBoolean("pending_remote_bundle", false);
}

共享首选项( E)"BundleLoader", (中文(简体) ), "pending_remote_bundle", (中文(简体) ), "active_remote_bundle")必须与定义在 BundleLoaderModule (单位:千美元)PREFS_NAME, (中文(简体) ), PREFS_PENDING_KEY, (中文(简体) ), PREFS_ACTIVE_KEY) (中文(简体) ).

证明

这是叉子 [email protected] 最初由Jusbrasil出版(2020-10-21,npm出版商) helielson,承诺 ec3d4520) (中文(简体) ). 上游 GitHub 重播时间 github.com/jusbrasil/react-native-bundle-loader 后来被删除。 通过 v.1.0 释放承诺保存完整的原始 git 历史; Android 执行是由 [email protected] 输入 mldb/react-native-bundle-loader 2021年出镜.

警卫

见 SECURITY.md 威胁模式、可接受的剩余风险和披露程序.

许可证

麻省理工学院

版本与发布记录

@exodus/react-native-bundle-loader 共发布 6 个版本,首次发布于 2026/05/09;过去 12 个月发布了 6 个版本。

版本发布日期距今
0.2.0-exodus.6latest2026/11/0211 天前
0.2.0-exodus.52026/09/032 个月前
0.2.0-exodus.42026/09/012 个月前
0.2.0-exodus.32026/06/025 个月前
0.2.0-exodus.22026/05/155 个月前
0.2.0-exodus.12026/05/096 个月前

关于 @exodus/react-native-bundle-loader 的常见问题

@exodus/react-native-bundle-loader 是什么?

装入远程反应原生JS捆绑.

如何安装 @exodus/react-native-bundle-loader?

在项目目录运行 npm install @exodus/react-native-bundle-loader,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>=16.8.0)、react-native(>=0.78.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@exodus/react-native-bundle-loader 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@exodus/react-native-bundle-loader 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@exodus/react-native-bundle-loader 需要什么版本的 Node.js?

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

@exodus/react-native-bundle-loader 可以商用吗?

@exodus/react-native-bundle-loader 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@exodus/react-native-bundle-loader 的下载量有多少?

根据 npm 官方下载统计,@exodus/react-native-bundle-loader 在 2026/10/14 – 2026/11/12 这 30 天内被下载 10,881 次,日均约 363 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@exodus/react-native-bundle-loader 还在维护吗?

最新版本 0.2.0-exodus.6 发布于 2026/11/02(11 天前);至今共发布 6 个版本,其中过去 12 个月发布了 6 个;首次发布于 2026/05/09。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。