首页
/ Expo 根级 CHANGELOG 深度解读:SDK 版本节奏、变更分类体系与发布工程流水线

Expo 根级 CHANGELOG 深度解读:SDK 版本节奏、变更分类体系与发布工程流水线

2026-09-06 14:47:44作者:胡唯隽

本仓库根目录的 CHANGELOG.md 是 Expo 客户端面向开发者的官方变更记录:它只收录随 SDK 一同发布的、需要用户知晓的变更,而不像多数项目那样记录每一笔提交。读完本文,你可以掌握 Expo SDK 的版本节奏(47 → 57 的发布时间表)、五级变更分类体系如何驱动 SemVer 版本号递增,并了解发布工程背后的自动化工具链——et merge-changelogs 如何把 packages 下数百个包的变更汇总进根 CHANGELOG,以及代码评审机器人如何自动补全条目链接。

一、文档定位:两级 Changelog 体系与 Unpublished 区

CHANGELOG.md 开头的原文说明了两条核心规则(见 CHANGELOG.md):

  • 这是 Expo client 的开发者可见变更记录;
  • 只有在某个 SDK 中发布的包级变更才会汇总到这里;未随 SDK 发布的包级变更,要等到发布前才追加进本文件,在此之前应查看 packages 目录中各包自己的 CHANGELOG。

由此形成两级体系:

  1. 包级 changelogpackages/expo-camera/CHANGELOG.md 等每个包各自维护,贡献者每次改动包时同步更新;
  2. 根级 CHANGELOG:只在 SDK 发布时由工具链合并生成,代表一次“SDK 快照”。

文件顶部常驻一个 ## Unpublished 区块(CHANGELOG.md#L6-L14),其中预置了三个空分类标题(### 📚 3rd party library updates### 🛠 Breaking changes### 🎉 New features### 🐛 Bug fixes),作为 SDK 发布前变更条目的暂存区。从源码 tools/src/Changelogs.ts#L87-L90 可以看到这一约定被固化为常量:

  • UNPUBLISHED_VERSION_NAME = 'Unpublished':未发布版本标题;
  • VERSION_EMPTY_PARAGRAPH_TEXT:当某版本没有用户可见变更时写入的占位说明。

解析规则在 tools/src/Changelogs.ts#L92-L100 中定义:二级标题(depth 2)表示一个版本,三级标题(depth 3)表示变更类型,列表项即条目。

二、版本时间线:从 SDK 47 到 SDK 57 的发布节奏

当前仓库 CHANGELOG 覆盖 13 个 SDK 主版本,标题格式统一为 ## <版本> — <发布日期>。按时间倒序整理如下:

SDK 版本 发布日期 与上一版间隔
57.0.0 2026-07-08 约 1 个月
56.0.0 2026-06-01 约 3.5 个月
55.0.0 2026-02-25 约 6 个月
54.0.0 2025-09-10 约 5 个月
53.0.0 2025-04-30 约 5.5 个月
52.0.0 2024-11-08 约 6 个月
51.0.0 2024-05-07 约 4.5 个月
50.0.0 2023-12-12 约 6 个月
49.0.0 2023-06-27 约 4.5 个月
48.0.0 2023-02-09 约 4 个月
47.0.0 2022-10-28

可以看到 Expo SDK 主版本大致保持每季度至半年一次的节奏;SDK 56 与 57 之间间隔明显缩短(2026-06-01 → 2026-07-08),说明 2026 年进入了一个更紧凑的发布周期。每个版本标题后跟随四个固定分类小节,下文第三节详解。

三、变更分类体系:六个标题如何驱动版本号

每个 SDK 版本下使用带 emoji 的三级标题对条目分组。这套分类并非纯展示——正如仓库贡献指南 guides/contributing/Updating Changelogs.md 所述,“把条目放进正确的分类很重要,因为发布脚本会依据分类决定包发布时如何递增版本号”。

分类在源码中是强类型枚举 tools/src/Changelogs.ts#L52-L82ChangeType),与文档一一对应:

分类标题 语义 版本递增含义
🛠 Breaking changes 需要用户改代码或项目配置的 API 变更 触发 major 递增
🎉 New features 非破坏性的公开 API 新增;内部新功能应放 Others 至少 minor 递增
🐛 Bug fixes 缺陷修复、澄清歧义的文档修正 patch 级别
⚠️ Notices 不兼容但保留向后兼容的弃用声明、边角行为变化 需用户知晓,通常 minor/patch
💡 Others 内部重构、构建工具、例行工作等 不直接影响用户
📚 3rd party library updates Expo Go 中升级的第三方库(如 react-native-reanimated 仅用于根 changelog

根 CHANGELOG 实际出现了前四类(57.0.0 版本同时包含 Breaking changes、New features、Bug fixes 与 Others,见 CHANGELOG.md#L18CHANGELOG.md#L29CHANGELOG.md#L114CHANGELOG.md#L234)。⚠️ Notices📚 3rd party library updates 在近期版本中按需出现——后者按指南约定“仅用于根 changelog”,用于记录 Expo Go 内第三方库的版本升级。

从源码结构看,发布脚本会读取每个包的 changelog 条目并按最高级别分类计算应发布的版本类型(见 tools/src/publish-packages/helpers.ts#L246-L257:基于包内变更“本应发布为某 ReleaseType”,若在 SDK 分支上发布则降级为 patch 递增)。换言之,分类写错会直接导致版本号升错——这是把条目归类当作强约束的原因。

四、SDK 57.0.0 实例剖析:一次完整 SDK 快照里有什么

以最新发布的 57.0.0(CHANGELOG.md#L16-L279)为例,完整走读一个版本的记录结构。

4.1 破坏性变更(Breaking changes)

57.0.0 列出 4 项,全部需要用户侧配合:

  1. @expo/ui(universal/android):文本输入改用 BasicTextField 组件,替代 Filled Material TextField——原生 UI 外观会发生可见变化;
  2. expo-modules-jsi(iOS):JavaScriptError 由“不可拷贝的 struct”改为“遵循 Error 的可拷贝 class”,且 JavaScriptValue 不再遵循 Error——原生模块作者需迁移 Swift API;
  3. expo-font(web):移除 Server.resetServerContext(),服务端字体状态改为经 AsyncLocalStorage 按渲染作用域隔离;
  4. @expo/cliexpo prebuild 默认清空并重新生成原生目录,需传 --no-clean 才能应用到已有目录。这是最容易被 CI 脚本踩中的行为变更:升级 CLI 后若构建脚本依赖增量 prebuild,需要显式传参。

4.2 新特性(New features)要点

  • 新包首发Initial release of @expo/require-utils——每个 SDK 中首次入列的包都会以这种固定句式登记;
  • CocoaPods Bundler 支持pod-install@expo/package-manager@expo/cli 三处同步支持 Bundler 托管的 CocoaPods 安装(同一 PR 在三包 changelog 中各记一条);
  • 实验性平台expo-modules-autolinking 新增实验性 tvos/macos 解析,@expo/cli 中由 expriments.outOfTreePlatforms 配置门控;
  • @expo/ui 批量扩充:iOS 侧新增大量 SwiftUI 修饰符(accessibilityHiddenaccessibilityIdentifierdynamicTypeSizelistRowSpacing、自定义 SF Symbols、strokeBorder 等),Jetpack Compose 侧新增 NavigationBar/NavigationBarItemBasicTextFieldonGloballyPositioned,web 侧 <Host> 支持 seedColorcolorScheme 覆盖;
  • expo-router:新增 standard-navigation 集成、从 expo-router/drawer 再导出抽屉内容组件、native-tabs 为 disabled 标签页发出 isPrevented: truetabPress 事件、原生 Stack 选项新增 unstable_nativeProps
  • expo-modules-core(iOS):新增 @Record 宏(免 @Field 包装直接由存储属性合成 record)、@Event 宏(把函数类型 var 变成类型化 JS 事件),且 @ExpoModule 可自动从类名合成模块 JS 名,Name(…) 定义项不再必填——这显著降低了原生模块作者的样板代码量;
  • 面向 JS 的新 APILinking.clearInitialURL() 重置缓存的深链 URL;expo-media-libraryQuery 支持按 isFavorite 过滤并新增 Query.exeForMetadata() 廉价批量拉取;expo-image-picker 允许在 iOS 模拟器调用 launchCameraAsyncexpo-contacts 表单新增 cancelButtonTitle/showsCancelButton/preventAnimation 选项;expo-networkexpo-clipboard 新增 macOS 平台支持。

4.3 缺陷修复(Bug fixes)

57.0.0 的修复条目按包分组,其中信息量最大的几条:

  • expo-sqliteuseLibSQL: true 在 Android 上的致命 JNI 崩溃——原因是 libSQL 会话绑定在 Kotlin 与默认原生绑定已切到 ByteBuffer 后仍声明 byte[] 签名;
  • expo-sensors:预构建的 ExpoSensors.xcframework 在 iOS 上永远返回 motion 权限 denied,根因是 prebuild 配置无条件定义了本应仅在显式 motionPermission: false 时才设置的 EXPO_DISABLE_MOTION_PERMISSION
  • expo-modules-core(iOS):修复新架构下按 r 触发的 reload 死锁(reloadAppAsync 已在主线程时改为同步触发 reload);修复 Updates.reloadAsync() 时旧 AppContext 提前析构导致的崩溃;
  • @expo/ui:数十条覆盖 SwiftUI/Compose 布局、手势、安全区、community/bottom-sheet 嵌套滚动等细节;
  • expo(fetch 层):Android 上 gzip/br/zstd 响应的解压修复、bodyUsed 在二次 clone Response 时泄漏、无 body 的 POST/PUT/PATCH 误发单个 0x00 字节等。

4.4 Others:内部变更与性能事实

Others 类包含对贡献者重要但对终端用户透明的改动,例如:

  • expo-modules-jsi(iOS)性能:同步 host function 不再每次调用分配 JavaScriptRef,实测 no-op @JS host 调用基线约快 10%;JavaScriptUnownedValue 缓存 immortal IRuntime 后,addNumbers 约快 16%、addStrings 约快 24%——CHANGELOG 中明确给出了测量口径,属于可核实的实现事实;
  • @expo/cli:DevTools 插件新增 serverEntryPoint(在 CLI 进程中处理 HTTP/WebSocket)与 bannerTitle(启动横幅标题);
  • expo-image-picker:包入口切换到 TypeScript 源码、仅输出声明文件。

4.5 对照:SDK 56.0.0 的平台基线抬升

56.0.0(CHANGELOG.md#L280-L389)的 Breaking changes 由约 40 条同构条目构成:几乎所有核心包统一“Bumped minimum iOS/tvOS version to 16.4, macOS to 13.4”(同一 PR 批量修改),外加三项重量级变更——expo-modules-core 用 Swift 编写的 ExpoModulesJSI 包替换了 Objective-C++ JSI 层(第三方原生模块必须迁移)、expo-file-systemcopy()/move()/write()/readBytes()/writeBytes() 变为异步(同步版本改用 *Sync() 后缀)、以及 expo-media-library/expo-contacts/expo-calendar 将面向对象新 API 提升到根导入、旧 API 迁至 */legacy 入口。升级 56 的项目应在 CI 中验证最低系统版本与同步/异步 API 调用点。

五、发布工程:merge-changelogs 与 changelogVersions.json

根 CHANGELOG 并非手工维护,而是 SDK 发布流程的一部分,相关实现集中在 tools/src

  1. et merge-changelogs(别名 mc:实现见 tools/src/commands/MergeChangelogs.ts。其工作流程为:
    • 读取根 CHANGELOG.mdchangelogVersions.json
    • 找到上一个已发布版本,用 semver.inc(previous, 'major') 计算下一个 SDK 版本,并预先把下一个版本的包版本映射初始化为上一个版本的拷贝(MergeChangelogs.ts#L44-L53);
    • 遍历所有非 private、且在 changelogVersions.json 中未被显式置 null 的包,收集“自上一 SDK 中捆绑版本之后”的全部条目(MergeChangelogs.ts#L57-L64);
    • 未曾在上一 SDK 出现的包会被询问是否做首发(initial release),确认后以 Initial release of <包名> 🥳 句式插入 New features 区(MergeChangelogs.ts#L126-L150)——这正是 57.0.0 中 @expo/require-utils 条目的来源;
    • 支持 --cut-off 选项在合并后截断根 changelog。
  2. changelogVersions.json:记录“每个 SDK 版本捆绑了哪些包的哪个版本”的快照矩阵。例如 57.0.0 下 expo-camera57.0.1@expo/cli57.0.6expo-modules-core57.0.3expo-router57.0.4changelogVersions.json#L2-L102);置 null 表示该包未捆绑进 SDK(如 expo-dev-client)。该文件同时是“自上一 SDK 以来收集哪些条目”的基准:合并时按上一版本记录的包版本号作为 fromVersion 起点(MergeChangelogs.ts#L113-L114)。
  3. canary 发布:SDK 版本化的包在 main 分支上 canary 时递增到下一 major(如 55.0.2 → 56.0.0),在 sdk-* 分支上只递增 patch(tools/src/publish-packages/tasks/publishCanary.ts#L239-L240)。

六、条目写作规范与自动化协作

结合 guides/contributing/Updating Changelogs.md 与仓库内实现,一个合格条目需满足:

  • 描述性强、简洁:让零上下文的读者也能看懂改动;
  • 位于 Unpublished 区正确分类下
  • 包含 PR 与作者链接,格式固定为 (#NNNNN by @author)
  • 只允许纯文本与链接:列表、表格、引用块、图片等 Markdown 元素一概不允许,迁移指南应写进 PR 描述或独立文档。

两个自动化环节降低了执行成本:

七、如何阅读这份 CHANGELOG

  • 升级 SDK 前:定位目标版本的 ### 🛠 Breaking changes 小节,逐条确认是否有命中你 API 面的条目(如 57 的 expo prebuild --no-cleanexpo-file-system 异步化在 56 的同类变更);
  • 排查“升级后行为变了”:先查对应版本的 Bug fixes 与 Others 小节,条目自带 PR 号可直接溯源提交;
  • 确认某 SDK 里各包的准确版本:查 changelogVersions.json 中对应 SDK 键下的映射,而不是只看根 CHANGELOG;
  • 关注下一 SDK 的动向:看 ## Unpublished 区——当前该区块为空,说明最近的变更都已归入 57.0.0。

这份根级 CHANGELOG 因此不仅是历史流水账,而是 Expo 发布契约的一部分:分类体系约束版本递增,changelogVersions.json 约束包集合快照,et merge-changelogs 与代码评审机器人则保证从包级 changelog 到 SDK 级记录的汇总过程可复现、可审计。

登录后查看全文
热门项目推荐
相关项目推荐