Skip to content

34 · 动态化与热更新 ​

一句话定位:热重载是开发期效率工具,热更新是线上不发版改代码,动态化是运行时换界面/配置。三件事经常被混为一谈,本篇把它们拆开,并给出"Flutter 到底能不能做线上热更新"的诚实答案。

开篇速览 ​

问题答案
前端对应物Web 的 HMR(热更新)、灰度发布 / 特性开关、微前端动态加载
核心结论Flutter 没有"官方一键热更新",方案碎片化且 iOS 受限;多数需求用"远程配置 + 灰度"就能解决
最容易踩的坑把开发期热重载当成线上热更新能力,方案评审直接翻车
一句话红线iOS 侧"动态下发可执行代码"违反 App Store 审核条款;Android 可行但收益/成本要算清楚

代码来源说明:HaierEnergy 当前 lib/ 未接入任何线上热更新 SDK,也不存在动态化框架。 本篇按"决策框架 + 可行方案"撰写,属通用知识篇,落地前需与运维/后端共同评审。


一、先把三个概念拆开 ​

名词阶段改什么是否发版谁用
热重载 hot reload开发期Dart 代码否开发者,秒级看到改动
热重启 hot restart开发期Dart 代码 + 状态否开发者
热更新 hot fix线上可执行代码否(下发补丁)线上用户
动态化线上界面结构 / 远程配置否线上用户,常配合灰度

开发期"热重载"与线上"热更新"是两套完全不同的技术:

  • 热重载依赖 Flutter 开发框架的增量编译,产物只在你本机;
  • 线上热更新需要把新的 Dart 代码打包成补丁下发到用户手机,涉及编译、签名、下发、回滚一整条链路。

二、Flutter 线上热更新的真实约束 ​

2.1 平台限制 ​

平台动态下发可执行代码说明
iOS❌ 禁止App Store 审核条款(3.3.2 / 2.5.2)不允许下载可执行代码;解释型/脚本型同样受限,唯一例外是苹果自己的 JavaScriptCore 沙箱
Android⚠️ 可行但有前提技术上可以把 Dart 补丁打进 APK 增量包,但要自己建下发/校验/回滚体系,Google Play 对"更新代码"也有政策要求
Web / 桌面✅ 可行本身就是远端资源,天然可更新,但要处理缓存与版本

2.2 为什么 Flutter 没有官方方案 ​

Dart 代码在 AOT(提前编译)模式下被编译成机器码,不能像 JS 一样直接下发源码再解释。可行的两条技术路线:

  1. 整包/增量包热更新:iOS 上不可行(审核),Android 上相当于自建"应用商店";
  2. 脚本化动态化:在 Flutter 里内嵌脚本引擎(如 QuickJS、Lua)跑部分逻辑,把"会变的规则"从原生代码里抽出来。

第一条成本高、收益不稳;第二条能覆盖的场景有限。这就是"方案碎片化"的根源。


三、真正常用的"轻动态化":远程配置 + 灰度 ​

大多数"想热更新"的需求,本质是不想发版就改行为。这类需求 80% 用"远程配置 + 特性开关"就能覆盖:

dart
// 伪代码:AppConfigRepository 拉取远程配置
final config = await configRepository.fetch();
if (config.isFeatureOn('device_energy_chart')) {
  // 新功能对部分用户灰度开放
}

3.1 常见远程配置能力 ​

需求方案是否算"热更新"
改开关、改文案、改接口地址远程配置 / 特性开关不算,但解决 80% 场景
改列表顺序、优先级下发结构化配置(JSON)不算
改图标、图片、主题下发静态资源(带版本号)弱动态化
改页面结构下发"界面描述"(JSON DSL 渲染)动态化,成本高
改业务逻辑代码Dart 补丁 / 脚本引擎热更新,成本最高

3.2 落地要点 ​

  • 开关一定要有兜底值:拉不到配置时走默认值,不能因配置失败影响核心流程;
  • 配置要版本化 + 灰度:按用户分桶,先 1% 再放量;
  • 本地缓存 + 增量拉取:别每次启动都全量下载;
  • 和埋点联动:开关曝光、命中、回滚都要有埋点,否则灰度决策没有数据。

四、真要上"动态化"怎么选(决策框架) ​

你的诉求推荐路线成本
改文案/开关/接口远程配置中心(自研或三方)低
改静态资源CDN 资源版本化 + 本地缓存低
改少量页面结构JSON DSL 渲染框架(自研,边界严格)中
只做 Android 的热修复增量包下发(自研链路)高
跨端一致的热更新不建议,维护两条原生补丁链路不现实极高

判断红线:如果团队连"远程配置 + 灰度 + 回滚"都还没建好,先别谈 Dart 补丁热更新。 优先级永远是:稳定发版流程 → 远程配置 → 灰度 → 资源动态化 → 代码热更新。


五、脚本引擎兜底(进阶) ​

对"规则频繁变化"的小段逻辑(如优惠计算、告警阈值),可以用脚本引擎把规则抽出来:

  • Android / iOS 双端内嵌 JS 引擎(如 quickjs Dart 绑定)跑一段规则脚本;
  • 脚本用远程下发,不触碰 iOS 审核红线(走 JavaScriptCore 类沙箱解释执行的灰色边界);
  • 只放"非核心、容错要求低"的逻辑,核心交易/安全逻辑绝不能脚本化。

代价:多一个运行时、性能损耗、调试困难、双端行为不一致风险。只有规则变更频率确实高才值得。


六、项目建议 ​

针对 HaierEnergy(能源 App,双端上架):

  1. 近期:把「远程配置 + 特性开关 + 灰度」补起来,先覆盖"接口地址、功能开关、文案";
  2. 中期:把图标/主题做成可下发的静态资源(带版本);
  3. 远期:若出现"算法规则频繁变更"的明确需求,评估脚本引擎方案;
  4. 不做:自建 Dart 补丁热更新(iOS 受限 + 双端不一致 + 运维成本高,收益不匹配)。

动手练习 ​

  1. 设计一个「远程配置中心」的 Dart 抽象接口(含本地缓存、兜底值、增量拉取、埋点四要素),写出关键类骨架;
  2. 用 Widget test 验证:配置拉取失败时页面按兜底值渲染,且不阻塞主流程;
  3. 给一个功能写"灰度开关 + 命中埋点"的最小实现。

自检清单 ​

  • [ ] 能区分热重载 / 热更新 / 动态化 / 远程配置
  • [ ] 能说清 iOS 为什么禁止动态下发可执行代码
  • [ ] 能在方案评审时给出"先远程配置、再资源动态化、最后才代码热更新"的排序
  • [ ] 知道远程配置落地四要素(缓存 / 兜底 / 灰度 / 埋点)

常见误区 ​

  • "Flutter 官方支持热更新":官方没有线上热更新能力,网上方案都是第三方/自研;
  • "Android 能热更,iOS 后面再说":双端行为不一致,方案评审时就要说清 iOS 怎么办;
  • "远程配置就是热更新":配置不改变可执行代码,两者定位完全不同;
  • "先上了再说":没有回滚和灰度,热更新一次事故比发版事故更致命。

相关文档 ​