如何让你的 OTA 更新包保持精简和快速
用更小的包体、更聪明的发布策略,以及优化 OTA 更新用量的实用技巧,更快地把 OTA 更新推送给你的 React Native 用户。
中文
复制

你有没有过这样的经历:PR 合并几分钟后,关键修复就推送到全部用户手里——从修复到用户指尖一路畅通,没有层层审批,也不打断用户体验。这种感觉永远不会腻。
EAS Update 让这一切成为可能,一旦体验过,就很难想象没有它该怎么工作。但发布修复只是故事的一半。只有更新真正跑在用户设备上,用户才能受益。下载完成得越快,修复就越早到用户手里;更新包越精简,所有人的体验就越好。下载更快、成本更低,团队和用户规模增长时也更顺畅。这篇指南会讲清楚如何让更新保持小巧、放心发布,并让每一次推送都物尽其用。
EAS Update 计费方式回顾
想用好 EAS Update,先理解两个决定更新如何分发和计费的概念会很有帮助:月活跃用户(MAU)和带宽。搞清楚它们的运作方式,以及你能做哪些事让更新更精简,就意味着用户下载更快、你的成本更低。本文假设你已经了解 EAS Update 是什么,以及它适用于哪些场景。
月活跃用户(MAU)
月活跃用户(MAU)指在单个计费周期内通过 EAS Update 下载过至少一次更新的唯一用户。无论这个月下载了多少次更新,都只算一个 MAU。如果用户检查了更新但没有新版本可下载(也就是没有发生下载),则完全不计入。
卸载后重新安装应用,下次下载更新时会算作一个新的 MAU。同样,同一个人使用两台不同设备会算作两个 MAU。这些边缘情况大概不会让你的数字发生剧烈变化,但如果你的实际 MAU 数量与 Play 和 App Store 的统计,或那些会考虑更多用户唯一身份信息的指标对不上,了解一下还是有必要的。
带宽
用户每下载一次更新都会消耗带宽,但每个套餐的配额都相当充裕,大多数团队的使用量都远在套餐额度之内。由于压缩等因素,实际带宽消耗往往比你预想的低得多。
下载体积取决于改动了什么。比如只更新了 JavaScript,用户就只下载这部分。原始构建(或上一次更新)已经装在设备上的资源不会重新下载。
这个数字通常比你想象的小:一周发布三次更新,而用户那一周只打开一次应用,他下载的就是一次更新,而不是三次。客户端只下载最新一次更新,其余的直接跳过。无论从速度还是带宽角度看这都是好事——用户不会下载多余的东西,更新送达更快,你也只为真正用到的部分付费。
话虽如此,让更新保持精简仍然值得做,理由不止账单一项:下载更快、用户流量消耗更少、整体体验更流畅。后面会详细展开。
有一件事我们特别兴奋:从 SDK 55 起,你可以选择启用 Hermes 字节码差分(目前处于 beta,欢迎试用!)。客户端不再下载完整的 JavaScript bundle,而是对已安装的内容应用二进制差分。这能大幅缩小下载体积,用户更快拿到更新,你也能更频繁地发版。我们正在通过这类工作,让更新的分发成本更低,同时让用户体验更好。
为什么更新越小越好
更新越小,用户下载越快,每次更新消耗的带宽也越少——无论是对你的 EAS Update 用量,还是对使用蜂窝网络或有限流量套餐的用户而言。bundle 更小还意味着构建更快;应用启动时加载更新,更精简的 bundle 也意味着启动更利落。而当 bundle 大到一定程度,你在开发时都能感觉到——每次改动都要处理一个巨大的 bundle,live reload 会开始变慢。下面是一些值得了解的做法。
优化你的资源文件
图片会迅速推高更新包的体积,因此发布前确认它们已经过优化是值得的。尽可能压缩图片能带来明显的效果——通常肉眼几乎看不出质量损失。图片越小,更新下载越快,用户消耗的数据也越少。
让资源文件回归资源文件
当一张图片是独立的资源文件时,它只会被下载一次(通过构建或更新),之后就一直留在设备上。如果它没有变化,后续更新会完全跳过它。但如果资源被内联进 JavaScript(比如 base64 编码的图片),它们就成了 bundle 的一部分,每次更新都得跟着一起走。
对于频繁变化、或者完全不在 bundle 里的图片,可以考虑用 CDN 配合 expo-image 来提供。它默认的缓存策略会在首次加载时把图片持久化到磁盘,之后它们的行为就和打包进来的资源一样(下载一次,跨应用重启复用),同时不会给更新增加任何体积。
选择哪些资源进入更新
项目里的资源并非都需要通过更新下发。借助 asset selection,你可以在 app config 里用文件匹配模式精确指定哪些资源应该被包含进来。其余的都留在原生构建里,永远不会从更新服务器下载。
比如,如果每次发版之间只有 app/images 目录下的图片会变化,你就可以把更新范围限定在这些图片上:
{
"expo": {
"updates": {
"assetPatternsToBeBundled": [
"app/images/**/*.png"
]
}
}
}
配置好匹配模式后,在发布前运行 npx expo-updates assets:verify。这样能确保发布更新时所有必需的资源都被包含进去——漏掉的资源将不可用,可能导致异常行为甚至崩溃。更多细节请查阅文档。
定期向应用商店提交
当你向应用商店提交新构建时,它会包含你最新的所有资源。这意味着此后发布的任何 OTA 更新只需要包含自该构建以来发生变化的部分,用户下载的内容因此更少。如果你最近新增了体积较大或多个资源,发布一个新构建是让后续更新保持精简的好办法。
一种行之有效的做法是:保持固定的二进制发版节奏(比如每月一次),中间的变化则通过 OTA 更新推送。用户能更频繁地拿到改进,而每次新的二进制版本又会为后续更新重置资源基线。
控制好 JavaScript bundle 的体积

JavaScript bundle 是你发布的每一次更新的一部分。Expo Atlas 这类工具可以直观地拆解 bundle,显示每个依赖占用了多少空间。结果往往出人意料——你可能会发现,为了一个工具函数引入的库,实际体积远超预期;也可能发现某个早已不再使用、却一直没删掉的依赖。
现在就可以用本地 dev server 试一下。启用 Atlas 启动应用:
EXPO_ATLAS=true npx expo start
然后在运行 dev server 的终端里按 shift + m 打开 dev tools 插件菜单,启动 Atlas。
高效地发布更新
前面讲的是如何让更新保持精简。但发布方式同样重要。rollout 让你能更好地控制更新触达用户的过程,而用更新做内部测试则可以省下时间和 build credits。
用 rollout 发布生产更新
发布更新时,默认会推送给全部用户。用户下载的每一次更新都会计入带宽用量,所以 rollout 是一种天然的省流量方式:先小范围放量,确认一切正常,再推给所有人。
用更新做内部测试和 PR 预览
开发过程中,想让团队看到改动并 review,通常得重新构建一个 build。但如果改动不涉及原生代码,OTA 更新能起到同样的作用——而且只需要几秒钟。团队可以直接在已经安装好的 development build 上预览改动,甚至可以通过自动生成的 pull request 预览来查看。
review 和迭代环节越多地通过更新而非 build 来完成,等待别人看到改动的时间就越短。团队几秒钟内就能在设备上拿到最新版本,反馈循环因此能更早启动。迭代一个功能时,这种收益累积得很快,同时还省下了 build credits。而且下载这些更新的只有你的团队,MAU 和带宽成本几乎为零,是套餐里性价比最高的省力手段之一。
跟踪用量
前面讲到的这些做法(精简更新、使用灰度发布、借助更新做内部测试),都能帮你从 EAS Update 套餐里榨出更多价值。不过,能随时看到进展如何,也是件好事。
在控制台里监控用量
Expo 在账户的 Billing 和 Usage 页面提供了用量概览。在那里你能看到 EAS Update 用量的汇总:当前计费周期(或指定时间段)内有多少 MAU 下载了更新,以及消耗了多少带宽。这是个随时掌握情况的好办法,尤其是刚发完一个大版本、想知道数据长什么样的时候。
用价格计算器找到合适的套餐
如果你还在纠结哪个订阅套餐适合自己,Expo 官网上的价格计算器可以帮忙。拖动滑块,填上预计的 MAU 数量、构建次数和 CI/CD 分钟数,它会给出套餐推荐——如果你的用量超出了该套餐包含的额度,它还会把超出部分的预估费用一并算出来。
先选适合当下的套餐
选套餐之前,你不需要把所有事情都想清楚。从满足当前需求的方案开始就行——应用增长得比预期快,也不会撞上墙。每个套餐都会随用量自然扩展,需求变了也可以随时切换。Expo 的定价就是要跟着你一起成长,而不是挡你的路。所有套餐都包含 EAS Update,免费套餐也不例外,所以你可以零负担地先试起来。
小结
说到底,EAS Update 给的条件相当不错:几秒内把更新推送给用户,和团队一起更快迭代,按自己的节奏扩展。精简更新、灰度发布,再加上前面提到的几招,就足以让用户用上更快的体验,同时让你的套餐更物有所值。
准备开始了?在项目里配置 EAS Update——几分钟就能搞定,配好就能立刻发更新。如果想深入了解前面提到的任何话题,EAS Update 文档是很好的下一步。也欢迎跟我们分享你用 EAS Update 的成功故事!