跳到主要内容
预计阅读 39 分钟

ReactNative篇 | 动态化容器

前言

前两章我们从底层架构的角度拆解了 RN 的老架构(Bridge)和新架构(Fabric + TurboModule)。但对于很多业务团队来说,选择 RN 而不是纯 Native 的最大理由,不是跨端,而是——动态化

什么是动态化?简单说就是不发版就能更新 App 的能力

想象一下:你刚上线的新功能有一个严重的 UI bug,如果是纯 Native 开发,你只能重新提交 App Store 审核,最快也要等 1-2 天。但如果你的 App 用了 RN 的动态化方案,你可以在几分钟内把修复后的 JSBundle 推送到用户手机上,无需重新发版。

这个能力对业务的价值是巨大的。但它也带来了一系列技术挑战:怎么拆包?怎么更新?怎么保证安全?怎么做增量?

面试中,“动态化”是 RN 高级岗位的高频考点。面试官想看的不仅是你会用 CodePush,更是你对整套动态化体系的理解深度。

本章,我们就从为什么需要动态化出发,完整拆解 JSBundle 拆包、热更新方案、增量更新、安全考量,最后和 Flutter 的动态化做个对比。


诊断自测

Q1:为什么 RN 可以做动态化,而 Native(Swift/Kotlin)不行?技术层面的根本原因是什么?

点击查看答案

根本原因在于 RN 的业务逻辑和 UI 描述是用 JavaScript 编写的,运行在 JS 引擎中。JS 代码以 JSBundle(本质是一个 .js.bundle 文件)的形式存在,可以像加载任何文件一样动态下载和加载。而 Native 代码(Swift/Kotlin)是编译成机器码的,修改后必须重新编译打包并通过应用商店分发。

从 iOS 政策角度看,Apple 明确禁止动态下载和执行原生可执行代码(如动态链接库),但允许解释执行的脚本(如 JavaScript),这也是 RN 动态化的政策基础。

Q2:什么是”基础包 + 业务包”的拆包策略?为什么要拆?

点击查看答案

RN 默认会把所有 JS 代码打成一个 JSBundle。“基础包 + 业务包”是将 JSBundle 拆成两部分:

  • 基础包(Common Bundle):包含 React、React Native 框架代码和公共组件,所有业务共用,变化频率低
  • 业务包(Business Bundle):只包含某个业务模块的代码,变化频率高

为什么要拆?两个核心原因:

  1. 减少热更新下载量:大部分更新只涉及业务代码,只下发业务包即可,不需要重新下载包含框架代码的基础包
  2. 支持多业务模块独立更新:不同业务团队可以独立发布自己的业务包,互不影响

Q3:CodePush 是什么?它有什么局限性?

点击查看答案

CodePush 是微软提供的一个 RN/Cordova 热更新云服务。它允许开发者直接将 JSBundle 更新推送到用户设备,无需通过 App Store/Google Play。

局限性:

  1. 只能更新 JS 代码和静态资源,不能更新 Native 代码(新增 Native Module 必须发版)
  2. 服务在海外,国内访问速度和稳定性不理想
  3. 微软已于 2025 年 3 月 31 日正式关闭 App Center(包括 CodePush),意味着依赖 CodePush 的项目需要迁移到其他方案
  4. 不支持细粒度的灰度发布和回滚策略(相比自建平台)

一、为什么需要动态化?

1.1 业务驱动

动态化不是炫技,它解决的是真实的业务痛点

痛点一:App Store 审核周期

iOS App Store 的审核通常需要 1-3 天,紧急情况下申请加急审核也要几小时到一天。如果线上有一个影响用户体验的 bug,等审核通过再更新,用户可能已经流失了。

痛点二:用户更新率

即使新版本已经上架,也不是所有用户都会立即更新。据统计,一个新版本要覆盖 90% 的用户,通常需要 1-2 周。动态化可以在用户无感知的情况下完成更新,覆盖率接近 100%。

痛点三:运营活动的灵活性

电商类 App 经常需要快速上线运营活动页面(双十一、618 等)。如果每次活动都要发版,节奏根本跟不上运营需求。动态化让前端团队可以像发布网页一样发布 App 内容。

痛点四:A/B 实验

动态化让 A/B 实验的成本大大降低。你可以在不发版的情况下,给不同用户推送不同版本的 JSBundle,快速验证功能方案。

1.2 技术可行性

RN 之所以能做动态化,核心在于它的运行时架构:

App 启动
  → 加载 JSBundle(一个 JS 文件)
  → JS 引擎(Hermes/JSC)解释执行
  → React 组件渲染为原生 UI

JSBundle 本质上就是一个文件。你可以把它打包在 App 内(离线包),也可以从服务器下载。只要替换了这个文件,App 重新加载后就能展现新的内容。

但有一个硬限制:动态化只能更新 JS 层的代码和静态资源(图片、字体等)。 如果你的更新涉及新增或修改 Native Module(原生代码),就必须走正常的发版流程。


二、JSBundle 拆包策略

默认情况下,RN 的 Metro bundler 会把所有 JS 代码打成一个 JSBundle。对于小型项目这没问题,但对于中大型项目,这个单一的 Bundle 可能会有几 MB 甚至十几 MB,每次热更新都全量下载显然不合理。

2.1 基础包 + 业务包

最常见的拆包策略是**“一个基础包 + 多个业务包”**:

┌─────────────────────┐
│    基础包 (Common)    │  React, RN, 公共组件, 公共工具
│    ~2MB, 很少变化     │
└─────────────────────┘

┌──────────┐ ┌──────────┐ ┌──────────┐
│ 业务包 A  │ │ 业务包 B  │ │ 业务包 C  │
│  首页模块  │ │  订单模块  │ │  我的模块  │
│  ~200KB  │ │  ~300KB  │ │  ~150KB  │
└──────────┘ └──────────┘ └──────────┘

基础包 包含:

  • reactreact-native 框架代码
  • 公共组件库(按钮、弹窗、表单等)
  • 公共工具函数(网络请求、存储、日志等)
  • 全局状态管理(Redux/MobX 等)

业务包 包含:

  • 某个业务模块的页面和逻辑
  • 该模块特有的组件和资源

2.2 拆包的技术实现

RN 官方的 Metro bundler 从 0.60+ 开始支持配置多入口和自定义序列化器,可以实现基础拆包。但大多数生产项目会使用更成熟的方案:

方案一:基于 Metro 自定义配置

通过修改 metro.config.js,配置 createModuleIdFactoryprocessModuleFilter,控制模块的分配:

// metro.config.js(简化示意)
module.exports = {
  serializer: {
    createModuleIdFactory() {
      // 自定义模块 ID 生成规则,确保基础包和业务包的模块 ID 不冲突
      return (path) => {
        // 基于文件路径生成稳定的模块 ID
        return hashModulePath(path);
      };
    },
    processModuleFilter(module) {
      // 打业务包时,过滤掉已经在基础包中的模块
      if (isBuildingBusinessBundle) {
        return !isInCommonBundle(module.path);
      }
      return true;
    }
  }
};

方案二:使用 Re.Pack(前身 Haul)

Re.Pack 是一个用 Webpack 替代 Metro 的方案,天然支持 Code Splitting 和 Module Federation。它允许你用 Webpack 生态的 SplitChunksPlugin 来做更灵活的拆包。

方案三:自建拆包工具

一些大厂(如携程、美团)会自建拆包工具链,根据业务模块的依赖关系自动分析和拆分。

2.3 运行时加载

拆包之后,运行时需要先加载基础包,再按需加载业务包:

App 启动
  → 加载基础包(内置在 App 中或从缓存读取)
  → JS 引擎初始化,React/RN 框架就绪
  → 用户进入某个业务模块
  → 动态加载对应的业务包
  → 业务模块渲染

动态加载业务包的方式通常是通过一个 Native Module 来实现:

// 简化示意
async function loadBusinessBundle(bundleName) {
  const bundlePath = await BundleManager.download(bundleName);
  await BundleManager.loadBundle(bundlePath);
  // 业务包中的组件现在可以使用了
}

三、热更新方案

热更新是动态化的核心能力。下面介绍几种主流方案。

3.1 CodePush(已停服)

CodePush 曾经是 RN 热更新的事实标准,由微软提供。

工作流程:

开发者发布更新
  → 上传新的 JSBundle 到 CodePush 服务器
  → App 启动时检查更新(或后台定时检查)
  → 发现新版本 → 下载 JSBundle
  → 下次启动时加载新的 JSBundle(或立即重启加载)

核心 API:

import CodePush from 'react-native-code-push';

// 方式一:装饰器方式,自动检查更新
const App = CodePush({
  checkFrequency: CodePush.CheckFrequency.ON_APP_START,
  installMode: CodePush.InstallMode.ON_NEXT_RESTART,
})(MyApp);

// 方式二:手动控制
async function checkUpdate() {
  const update = await CodePush.checkForUpdate();
  if (update) {
    await update.download();
    await CodePush.restartApp();
  }
}

重要提醒: 微软的 App Center(包括 CodePush 服务)已于 2025 年 3 月 31 日正式关闭。目前社区有以下替代方案:

  • EAS Update(Expo):如果你使用 Expo,这是官方推荐的替代方案
  • react-native-ota-hot-update:社区开源的自托管方案
  • 自建热更新平台:大中型团队的首选

3.2 自建热更新平台

对于中大型团队,自建热更新平台是更常见的选择。它提供了更高的灵活性和控制力。

一个典型的自建平台包含以下组件:

┌─────────────────────────────────────┐
│          管理后台 (Web)               │
│  - Bundle 版本管理                    │
│  - 灰度发布配置                       │
│  - 回滚操作                          │
│  - 发布审批流程                       │
│  - 数据统计(更新率、成功率、崩溃率)    │
└────────────────┬────────────────────┘

┌────────────────▼────────────────────┐
│          Bundle 服务器               │
│  - Bundle 存储(OSS/CDN)            │
│  - 版本管理 API                      │
│  - 差分计算服务                       │
│  - 签名验证                          │
└────────────────┬────────────────────┘

┌────────────────▼────────────────────┐
│          App 端 SDK                  │
│  - 启动时/后台检查更新               │
│  - 下载 + 验签 + 解压               │
│  - 加载新 Bundle                    │
│  - 异常回滚(加载失败自动回退旧版本)  │
└─────────────────────────────────────┘

核心设计要点:

  1. 版本管理:每个 Bundle 有唯一版本号,和 App 的 Native 版本绑定(确保 JS 版本和 Native 版本兼容)
  2. 灰度发布:先推送给 1% 的用户,观察崩溃率和异常数据,没问题再逐步扩大
  3. 回滚机制:发现问题时,可以在后台一键回滚到上一个稳定版本
  4. 强制更新/静默更新:根据更新内容的重要性选择策略

3.3 更新时机策略

热更新的时机选择也很重要,常见策略:

策略说明适用场景
启动时静默更新App 启动时后台下载,下次启动生效非紧急更新
启动时强制更新App 启动时下载并立即重启严重 bug 修复
后台定时检查定时轮询服务器,发现新版本就下载一般场景
进入模块前检查用户进入某个模块时,检查该模块的业务包是否有更新拆包场景
手动触发在设置页提供”检查更新”按钮用户主动行为

四、增量更新与差分下载

全量下载 JSBundle 在包体较大时会消耗较多流量,特别是对于”只改了一行代码”的小修复。增量更新就是为了解决这个问题。

4.1 差分算法

增量更新的核心是差分算法:计算新旧 JSBundle 之间的差异,只下发差异部分(patch)。

常用的差分算法:

  • bsdiff:二进制差分算法,压缩率高,适合任意二进制文件
  • google-diff-match-patch:文本差分算法,更适合 JS 代码这种文本文件

流程:

服务端:
  旧 Bundle (v1.0) + 新 Bundle (v1.1) → 差分算法 → Patch 文件 (~20KB)

客户端:
  本地 Bundle (v1.0) + Patch 文件 → 合并算法 → 新 Bundle (v1.1)

假设完整的业务包是 300KB,而某次更新只修改了几个组件,差分后的 Patch 可能只有 10-30KB。下载量减少了 90% 以上。

4.2 增量更新的注意事项

版本链管理:

差分是基于”某个特定旧版本”计算的。如果用户本地的版本是 v1.0,服务端只有 v1.0 → v1.2 的 patch,但用户错过了 v1.1,直接下发 v1.0 → v1.2 的 patch 就行了。但如果服务端只有 v1.1 → v1.2 的 patch,用户就没法增量更新了。

常见做法是:服务端为最近 N 个版本都生成差分包,覆盖大部分用户。如果用户版本太旧,回退到全量下载。

服务端存储:
  v1.0 → v1.3 patch
  v1.1 → v1.3 patch
  v1.2 → v1.3 patch
  v1.3 全量包(兜底)

完整性校验:

客户端合并 Patch 后,必须对新 Bundle 做完整性校验(通常是比对 hash),确保合并结果正确。如果校验失败,回退到全量下载。


五、动态化的安全考量

动态化意味着你在用户的手机上执行从网络下载的代码——这对安全的要求非常高。

5.1 传输安全

必须使用 HTTPS。所有 Bundle 的下载必须通过 HTTPS,防止中间人篡改。这是最基本的要求。

证书固定(Certificate Pinning)。对于安全性要求更高的 App,建议在客户端固定服务器证书,防止中间人使用伪造证书进行攻击。

5.2 代码签名

这是动态化安全的核心。即使传输过程是安全的,你还需要确保下载的 Bundle 确实是你的服务器发出的,没有被篡改。

签名流程:

发布端:
  JSBundle → 计算 Hash → 用私钥签名 → 发布 Bundle + 签名

客户端:
  下载 Bundle + 签名 → 用公钥验签 → 签名合法 → 加载 Bundle
                                    → 签名非法 → 拒绝加载,报警

通常使用 RSA 或 ECDSA 非对称加密。私钥只在发布服务器上保存,公钥内置在 App 中。

5.3 代码混淆与加密

JSBundle 是 JavaScript 代码,下载到用户设备后理论上是可以被反编译阅读的。为了保护商业逻辑:

  • 代码混淆:使用 Metro 的 minify 或第三方混淆工具,让代码难以阅读
  • Hermes 字节码:使用 Hermes 引擎的 AOT 编译,将 JS 编译成 Hermes 字节码(.hbc 文件),不仅执行更快,也增加了逆向难度
  • 整体加密:有些方案会对 Bundle 整体加密,加载时在内存中解密

5.4 灰度发布与回滚

从安全和稳定性角度看,灰度发布和快速回滚是必不可少的:

  • 灰度发布:新版本先推给小比例用户(1%→5%→20%→100%),监控崩溃率和异常数据
  • 自动回滚:如果新 Bundle 加载失败(白屏、崩溃),客户端 SDK 应自动回退到上一个稳定版本
  • 服务端回滚:运营人员可以在后台一键将所有用户回退到指定版本

5.5 Apple 政策风险

Apple 的 App Store 审核指南第 3.3.2 条允许下载和执行 JavaScript 代码,但有一些限制:

  • 下载的代码不能改变 App 的主要功能和用途
  • 不能绕过审核引入被禁止的功能
  • 不能下载原生可执行代码(动态链接库等)

在实践中,大多数正常的热更新(bug 修复、UI 调整、功能微调)是被允许的。但如果你用热更新做了很激进的事情(比如上线了一个审核时完全不存在的功能),有被下架的风险。


六、与 Flutter 动态化的对比

面试中经常会被问到 RN 和 Flutter 在动态化方面的对比。这是一个能体现你技术视野的问题。

6.1 Flutter 的动态化困境

Flutter 使用 Dart 语言,在 Release 模式下会被 AOT 编译成原生机器码。这意味着:

  • Dart 代码不能像 JS 那样动态下载和解释执行
  • Apple 禁止动态下载原生可执行代码,所以在 iOS 上,Flutter 的 Dart 代码不能做热更新

这是 Flutter 相比 RN 最大的劣势之一。

6.2 Flutter 社区的尝试

虽然 Flutter 官方不支持动态化,但社区做了不少尝试:

方案原理局限性
Shorebird(官方相关)Dart 代码差分更新 + 运行时修补2024 年后逐渐成熟,但 iOS 端仍有政策风险
Fair(58同城)将 DSL 编译为 JSON,运行时解析渲染功能受限,不能覆盖所有 Widget
MXFlutter(腾讯)用 JS 编写 Flutter UI,JS 引擎解析已停止维护
动态 Widget服务端下发 Widget 描述 JSON只能做简单 UI,不能下发逻辑

6.3 对比总结

维度React NativeFlutter
动态化可行性天然支持(JS 解释执行)非常困难(Dart AOT 编译)
iOS 政策风险低(Apple 允许 JS 动态下载)高(Apple 禁止动态下载原生代码)
热更新生态成熟(CodePush 替代方案、自建平台)不成熟(社区方案各有局限)
更新粒度可以更新完整的业务逻辑 + UI大多只能更新 UI 描述
更新包大小JSBundle 通常较大,但支持增量原生代码差分更新效率高

面试中的回答思路: 如果被问到”RN 和 Flutter 怎么选”,动态化是 RN 的核心优势之一。对于需要频繁更新、运营驱动的业务(如电商、社交),RN 的动态化能力是 Flutter 很难替代的。


常见误区

误区一:“热更新可以更新所有东西”

热更新只能更新 JS 代码和静态资源(图片、字体、JSON 等)。如果你的更新涉及新增 Native Module(比如接入一个新的相机 SDK)、修改原生配置(如 Info.plistAndroidManifest.xml)、升级 RN 版本、修改原生代码,都必须走正常的发版流程。一个常见的坑是:开发者在热更新包中引用了一个只在新版 Native 代码中才有的 Native Module,导致老版本 App 加载热更新后直接崩溃。

误区二:“CodePush 是唯一的热更新方案”

CodePush 曾经是最流行的方案,但它只是众多方案中的一个,而且已经停服。对于国内的生产环境,自建热更新平台才是主流选择。自建平台可以根据业务需求定制灰度策略、审批流程、数据统计等功能,灵活性远超第三方服务。不要把”热更新”和”CodePush”画等号。

误区三:“拆包只是为了减少下载量”

减少下载量是拆包的好处之一,但不是唯一的原因。拆包还解决了以下问题:

  • 多团队协作:不同业务团队可以独立开发、独立发布自己的业务包
  • 按需加载:用户只加载当前需要的业务模块,减少内存占用和启动时间
  • 故障隔离:某个业务包出问题,只影响该模块,不会拖垮整个 App

误区四:“Flutter 完全不能做动态化”

Flutter 的动态化确实比 RN 困难得多,但并非完全不可能。Shorebird 等方案正在逐步成熟,Android 端的动态化限制也比 iOS 松得多。更准确的说法是:Flutter 在 iOS 上做代码级别的动态化有严格的政策限制,而 UI 级别的动态化(服务端下发 Widget 描述)是可行但功能受限的。


小结

本章从动态化的业务价值出发,系统梳理了 RN 动态化容器的完整技术体系。

核心要点

  1. 动态化的本质:利用 JS 解释执行的特性,通过替换 JSBundle 文件实现不发版更新
  2. 拆包策略:基础包(框架 + 公共代码)+ 业务包(模块代码),减少更新量、支持独立发布
  3. 热更新方案:CodePush 已停服,自建平台是主流,需要关注灰度、回滚、监控
  4. 增量更新:差分算法生成 Patch,客户端合并 + 校验,减少 90%+ 下载量
  5. 安全体系:HTTPS + 代码签名 + 混淆/加密 + 灰度/回滚
  6. vs Flutter:RN 天然支持动态化是其核心优势,Flutter 受限于 Dart AOT 编译和 iOS 政策

记忆口诀

动态化四部曲:拆(拆包)→ 传(安全传输)→ 更(增量更新)→ 灰(灰度发布)


本章思维导图

RN 动态化容器
  • 为什么需要动态化
    • 绕过 App Store 审核周期
    • 提高用户覆盖率(静默更新)
    • 运营活动灵活上线
    • A/B 实验低成本实施
    • 技术基础:JS 解释执行
  • JSBundle 拆包
    • 基础包 + 业务包
    • 拆包实现:Metro 自定义 / Re.Pack / 自建工具
    • 运行时按需加载
  • 热更新方案
    • CodePush(已停服 2025.03)
    • 自建平台(管理后台 + Bundle 服务器 + SDK)
    • 更新时机策略(静默/强制/定时/按需/手动)
  • 增量更新
    • 差分算法(bsdiff / google-diff-match-patch)
    • 版本链管理
    • 完整性校验
  • 安全考量
    • HTTPS + 证书固定
    • 代码签名(RSA/ECDSA)
    • 代码混淆 + Hermes 字节码
    • 灰度发布 + 自动回滚
    • Apple 政策合规
  • vs Flutter 动态化
    • RN:天然支持(JS 解释执行)
    • Flutter:困难(Dart AOT + iOS 政策限制)
    • Flutter 社区方案(Shorebird / Fair / 动态 Widget)

练习挑战

第一题 ⭐(基础):概念辨析

请解释”热更新”和”热重载(Hot Reload)“的区别。它们在技术实现和使用场景上有什么不同?

点击查看答案

热更新(Hot Update / OTA Update):

  • 指在用户设备上,不通过应用商店,动态下载并替换 JSBundle 的过程
  • 发生在生产环境,面向终端用户
  • 更新后通常需要重新加载 App(或重新加载 JS 引擎)
  • 目的是修复线上 bug、发布新功能

热重载(Hot Reload / Fast Refresh):

  • 指在开发过程中,修改代码后不需要重新编译整个 App,只替换修改的模块
  • 发生在开发环境,面向开发者
  • 保留组件的当前状态,只更新变化的部分
  • 目的是提高开发效率

两者名字很像但完全是两回事:热更新是生产环境的部署机制,热重载是开发环境的调试工具。

第二题 ⭐⭐(进阶):方案设计

你负责一个 RN 电商 App,有首页、商品详情、购物车、我的四个主要模块。请设计一个拆包和热更新策略,考虑以下因素:

  • 首页变化最频繁(运营活动)
  • 购物车模块对稳定性要求最高
  • 团队有 4 个小组,各负责一个模块
点击查看答案

拆包策略:

基础包:React + RN + 公共组件 + 网络库 + 状态管理 + 路由

业务包 × 4:
  - home.bundle(首页模块)
  - product.bundle(商品详情模块)
  - cart.bundle(购物车模块)
  - profile.bundle(我的模块)

每个业务包独立打包、独立发布、独立版本管理。4 个团队可以各自按自己的节奏发布。

热更新策略:

模块更新策略灰度策略回滚
首页高频更新,启动时静默下载1%→10%→50%→100%,每步观察 1 小时自动回滚(崩溃率 > 0.5%)
商品详情中频更新,后台定时检查1%→20%→100%,每步观察 2 小时自动回滚(崩溃率 > 0.3%)
购物车低频更新,稳定性优先1%→5%→20%→50%→100%,每步观察 4 小时自动回滚(崩溃率 > 0.1%),且需人工审批才能全量
我的中频更新,后台定时检查1%→20%→100%自动回滚

其他设计:

  • 基础包内置在 App 中,只在大版本更新时变化(跟随发版)
  • 所有业务包支持增量更新(bsdiff),减少用户流量消耗
  • 购物车模块因为涉及支付,安全等级最高,加签名验证 + 代码混淆
  • 首页模块支持”预加载”,在 WiFi 环境下提前下载下一个版本

第三题 ⭐⭐⭐(综合):故障排查

线上用户反馈:热更新后 App 启动白屏。你作为技术负责人,请列出可能的原因(至少 5 个),并为每个原因提供排查思路和预防措施。

点击查看答案

原因一:JSBundle 下载不完整

  • 排查:检查下载日志,对比本地文件 hash 和服务端 hash
  • 预防:下载完成后做 hash 校验,校验失败则重新下载或回退

原因二:增量合并失败

  • 排查:检查 Patch 合并的日志,对比合并后 Bundle 的 hash
  • 预防:合并后做完整性校验,校验失败回退到全量下载

原因三:JS 和 Native 版本不兼容

  • 排查:检查热更新包是否引用了当前 App Native 版本不存在的 Native Module
  • 预防:热更新包绑定 Native 版本号,服务端下发时校验兼容性

原因四:Bundle 中存在 JS 运行时错误

  • 排查:查看 JS 错误日志(ErrorBoundary 捕获的错误、全局错误处理器的日志)
  • 预防:发布前自动化测试,灰度阶段监控 JS 错误率

原因五:Hermes 字节码版本不匹配

  • 排查:如果热更新包是 Hermes 字节码(.hbc),检查编译时使用的 Hermes 版本和 App 内置的 Hermes 引擎版本是否一致
  • 预防:CI/CD 流程中确保使用与 App 相同版本的 Hermes 编译器

原因六:存储空间不足

  • 排查:检查设备可用存储空间,检查 Bundle 写入是否成功
  • 预防:下载前检查可用空间,空间不足时跳过更新

原因七:自动回滚机制未生效

  • 排查:检查回滚逻辑的日志,确认回滚触发条件和执行结果
  • 预防:实现”加载计数器”机制——新 Bundle 加载失败超过 N 次,自动回退到旧版本

自我检测

读完本章后,对照下面的清单检验一下自己的掌握程度:

  • 能说清楚为什么 RN 可以做动态化而 Native App 不行——从技术和政策两个角度
  • 能解释”基础包 + 业务包”的拆包策略:各包含什么、为什么要拆、怎么实现
  • 能说出至少两种热更新方案(CodePush、自建平台),并说明各自的优缺点
  • 能解释增量更新的原理:差分算法、版本链管理、完整性校验
  • 能列出动态化的安全考量清单:HTTPS、代码签名、混淆/加密、灰度/回滚
  • 能从动态化角度对比 RN 和 Flutter,并说出 Flutter 动态化困难的根本原因
  • 能设计一个适合自己项目的拆包和热更新策略
  • 了解 Apple App Store 对动态化的政策限制,知道什么能做、什么不能做

购买课程解锁全部内容

大厂前端面试通关:71 篇构建完整知识体系

¥89.90