expo-migrate-module 是什么?
expo-migrate-module 是 Expo 官方技能集 expo/skills 中的一个实验性 Skill,属于 expo-experiments 插件[citation:10][citation:17]。它的官方描述非常明确:“Migrate an existing Apple/Swift Expo native module from the Expo Modules API 1.0 definition DSL to the 2.0 macro API (sometimes called v2) while preserving its JavaScript and TypeScript contract.”[citation:1][citation:5]
它解决的是一个非常具体的迁移场景:你有一个用 1.0 DSL 写的 Swift Expo 模块,现在想用 2.0 宏重写。但 2.0 宏 API 不是 1.0 的超集——有些东西 2.0 还不支持,有些东西迁移后会改变 JS 侧的行为。这个 Skill 的作用就是让 AI 助手按照一套严格的纪律来完成迁移,而不是凭感觉改代码。
它的适用范围被严格限定:只处理 Swift 侧,只处理现有模块,不碰 Kotlin[citation:2][citation:6]。官方文档明确说“Do not use for creating a new module, general Expo SDK upgrades, or Android/Kotlin migrations”[citation:1]。
expo-migrate-module 的主要功能
契约清点(Establish the contract)。迁移前,Skill 要求 AI 逐一列出模块对外暴露的每一项:模块和共享对象的 JS 名称、函数名和参数签名(包括默认值、可空性、同步/异步行为)、属性名和可变性、事件线名和负载形状、记录字段的必填性、共享对象的构造方式、生命周期钩子等[citation:2][citation:6]。
2.0 表面验证(Verify the available 2.0 surface)。不是所有 1.0 的特性在 2.0 里都有对应宏。Skill 要求 AI 检查实际安装的 expo-modules-core 版本,把每一项分类为:可迁移(宏和运行时支持都存在)、保留在 DSL(2.0 没有等价物,或混合模式更安全)、阻塞(迁移会改变 JS 契约或缺少运行时支持)[citation:2][citation:16]。
分组迁移(Apply the migration)。按语义组逐一迁移:模块命名 → 函数 → 属性/常量 → 事件 → 共享对象 → 记录。每一组迁移后都保持 diff 尽量小,方便审查和回滚[citation:2][citation:6]。
混合模式支持。当某个成员无法迁移时,它可以继续留在 1.0 的 definition() DSL 里,与 @ExpoModule 宏共存。只有当 definition() 为空且模块名被 @ExpoModule 保留时,才删除它[citation:2][citation:16]。
expo-migrate-module 的核心特色
契约优先,语法迁移不改变行为。这是最根本的纪律。Skill 明确要求:不要“顺便改进”必填性,不要重命名事件,不要改变同步行为,不要改变队列语义。迁移的目标是让 JS 侧完全感知不到变化,而不是让代码“更现代”[citation:2][citation:6]。
基于实际安装版本验证,不靠猜。Skill 要求 AI 检查目标项目实际使用的 expo-modules-core 版本和宏声明,而不是根据 SDK 版本号推断。因为即使在 57.x 内,宏和核心接口也可能有差异[citation:2][citation:15]。
事件线名的陷阱。@Event 的默认线名会去掉开头的 on 并小写化剩余部分。比如 Swift 侧的 onProgress 默认会变成 JS 侧的 progress。如果要保持原有的 onProgress 监听器契约,必须显式传入 @Event("onProgress")[citation:16]。
版本门槛明确。2.0 宏要求 expo 57.0.7 或更新版本[citation:2][citation:16]。Skill 会检查 package.json 或 npm ls expo,版本不够时会停下来要求先升级,不会尝试在不支持的版本上迁移[citation:2][citation:6]。
expo-migrate-module 可以用来做什么?
把现有的 Swift 模块迁移到 2.0 宏。你维护一个用 1.0 DSL 写的 Expo 模块,升级到了 expo 57.0.7+,想把 Swift 侧改成 @ExpoModule、@JS、@Event 的写法,同时确保 JS 侧调用方不需要改任何代码。
增量迁移,混合共存。如果模块里有 2.0 暂时不支持的特性(比如队列绑定的异步函数、视图、联合类型、同步事件),可以先迁移能迁的部分,剩下的留在 definition() 里,两者共存[citation:16]。
排查迁移中的契约变化。如果你手动迁移时遇到了 JS 侧行为变化(事件名不对、参数签名变了),可以用这个 Skill 来检查迁移步骤是否遗漏了契约保持的约束。
不适合的场景:创建新模块(用 expo-module)、Expo SDK 版本升级(用 expo-upgrade)、Android/Kotlin 迁移(明确排除)[citation:1][citation:5]。
expo-migrate-module 适合哪些人?
- 维护 Expo 原生模块的 Swift 开发者:手上有一个用 1.0 DSL 写的模块,想采用 2.0 宏 API,但担心破坏 JS 侧的兼容性。
- 使用 Claude Code、Codex 或 Cursor 的 Expo 开发者:这些 Agent 都支持 Agent Skills 标准,安装后 AI 会自动在相关任务中加载这个 Skill[citation:8][citation:14]。
- 需要“契约保持”迁移纪律的团队:不想在迁移过程中意外改变 JS API 的行为。
不太适合:没有 Swift 模块需要迁移的人;只想升级 Expo SDK 版本的人(用 expo-upgrade);需要迁移 Kotlin 模块的人(Skill 明确不覆盖)[citation:1]。
expo-migrate-module 如何安装?
expo-migrate-module 属于 Expo 官方的 expo-experiments 插件,安装方式取决于你的 Agent。
方式一:Claude Code 插件安装(推荐)
claude plugin install expo@claude-plugins-official
这会安装 Expo 官方插件,包含所有 Expo Skills,包括实验性的 expo-migrate-module[citation:8][citation:14]。
方式二:Codex 插件安装
codex plugin add expo@openai-curated
[citation:8][citation:13]
方式三:通过 skills CLI 单独安装(适用于 Cursor 等其他 Agent)
npx skills add expo/skills --skill expo-migrate-module
或者通过 skills.sh 的通用安装命令[citation:16]:
npx skills add https://github.com/expo/skills --skill expo-migrate-module
方式四:从 expo/skills 仓库手动克隆
git clone https://github.com/expo/skills.git ~/.claude/skills/expo-migrate-module
[citation:9]
前置条件:目标项目的 expo 版本必须 ≥ 57.0.7。Skill 会在迁移前检查这个版本门槛[citation:2][citation:16]。
expo-migrate-module 怎么使用?
基本流程(三步):
- 建立契约。让 AI 读取模块的 Swift 源代码、TypeScript 声明、JS 调用点和测试文件,列出所有对外暴露的 API 项及其行为特征(默认值、可空性、同步/异步、事件名)。
- 验证 2.0 支持。检查实际安装的
expo-modules-core版本,确认每一项在 2.0 中是否有对应的宏和运行时支持。分类为可迁移、保留 DSL 或阻塞。 - 分组迁移。按模块命名、函数、属性/常量、事件、共享对象、记录的顺序逐一迁移。每迁移一组,保持 diff 可控,最后用示例应用验证。
推荐的触发方式:
使用 expo-migrate-module 技能,把这个模块的 Swift 实现从 1.0 definition DSL 迁移到 2.0 宏 API,保持 JavaScript 契约不变。
或者更具体:
用 expo-migrate-module 迁移这个模块的 Swift 侧。先检查 expo 版本是否满足 57.0.7+,然后清点所有 JS 可见的导出项,再逐组迁移。
[citation:16]
expo-migrate-module 使用技巧
不要跳过“契约清点”步骤。这是整个 Skill 最重要的纪律。如果 AI 直接开始写 @ExpoModule 代码,它可能会无意中改变某个参数的默认值或事件名。先让它输出完整的导出项清单,你确认后再迁移。
注意 @Event 的线名变化。@Event 默认会把 onProgress 变成 progress。如果你的 JS 侧监听的是 onProgress,必须显式写 @Event("onProgress") 来保持契约[citation:16]。
队列绑定的异步函数不要直接迁移。如果 1.0 DSL 里的异步函数绑定了特定队列,直接改成 2.0 的 async 宏可能会改变执行上下文。Skill 建议重构到 Swift Concurrency,或通过 continuation 调度回原队列[citation:2]。
混合模式是特性,不是缺陷。如果某个成员暂时无法迁移,留在 definition() 里和 @ExpoModule 共存是完全合理的做法。不要为了“完全迁移”而强行转换不支持的成员[citation:16]。
expo-migrate-module 收费吗?
免费且开源。expo-migrate-module 是 expo/skills 仓库的一部分,采用 MIT 许可证[citation:6][citation:17]。Skill 本身不收取费用,你只需要为使用的 AI 编码助手(Claude Code、Codex、Cursor 等)支付订阅费用。
expo-migrate-module 的优点与缺点
| 维度 | 优点 | 缺点 |
|---|---|---|
| 契约保护 | 强制清点和保持所有 JS 可见的 API 行为,防止迁移中意外破坏兼容性 | 需要 AI 花时间做完整清点,迁移速度比“直接改”慢 |
| 版本验证 | 基于实际安装的 expo-modules-core 验证宏支持,不靠 SDK 号猜测 |
要求 expo ≥ 57.0.7,旧版本项目需要先升级才能使用 |
| 混合模式 | 支持 1.0 DSL 和 2.0 宏共存,允许增量迁移 | 混合模式需要理解两种写法的边界,复杂度比全量迁移高 |
| 实验性状态 | 跟随 Expo 官方最新的 2.0 宏 API 指引 | 属于 expo-experiments,API 可能变化;SDK 57 中宏仍是实验性、无文档状态[citation:15] |
| 范围限定 | 只做一件事,不越界到新模块创建或 SDK 升级 | 不处理 Kotlin 迁移;如果模块有 Android 侧,需要单独处理 |
expo-migrate-module 与同类工具对比
vs 手动迁移。手动迁移需要你自己记住所有需要保持的 JS 契约点。Skill 把这些清点步骤固化为 AI 的强制流程,减少遗漏。
vs expo-module(创建新模块的 Skill)。expo-module 负责从零创建模块,讲解 1.0 DSL 和 2.0 宏的用法。expo-migrate-module 专门处理已有模块的迁移,两者的使用场景不重叠。官方文档明确指引:“Not for migrating an existing Swift module from the definition DSL to the Expo Modules API 2.0 macros; use expo-migrate-module for that.”[citation:3][citation:4]
vs expo-upgrade(SDK 升级 Skill)。expo-upgrade 处理整个项目的 Expo SDK 版本升级、依赖冲突和缓存清理。expo-migrate-module 不碰 SDK 版本,只处理模块内部的 API 写法迁移[citation:10]。
expo-migrate-module 常见问题 FAQ
Q:expo-migrate-module 和 expo-module 有什么区别?
expo-module 用于创建新的原生模块,涵盖 1.0 DSL 和 2.0 宏的用法。expo-migrate-module 用于把已有的 1.0 DSL 模块迁移到 2.0 宏,同时保持 JS 契约不变[citation:3][citation:4]。
Q:它支持迁移 Android/Kotlin 模块吗?
不支持。Skill 明确排除 Kotlin 迁移,Kotlin 侧继续留在 1.0 DSL[citation:1][citation:2]。
Q:需要什么 Expo 版本?
2.0 宏要求 expo ≥ 57.0.7。Skill 会检查版本,不够时会要求先升级[citation:2][citation:16]。
Q:为什么迁移后事件名变了?
@Event 的默认线名会去掉开头的 on 并小写化。要保留原事件名,显式传入,例如 @Event("onProgress")[citation:16]。
Q:如果有些成员无法迁移到 2.0 怎么办?
留在 1.0 的 definition() DSL 里,与 @ExpoModule 混合共存。当 definition() 为空且模块名被宏保留时,才删除它[citation:2][citation:16]。
expo-migrate-module 综合评价
expo-migrate-module 解决的是一个高风险迁移场景中的纪律问题:把 Swift 模块从 1.0 DSL 迁到 2.0 宏 API,同时不破坏 JS 侧的兼容性。
它的价值不在于“帮 AI 写 @ExpoModule 代码”——那很简单。它的价值在于强制 AI 先清点契约、再验证支持、最后分组迁移。手动迁移最容易犯的错误就是“顺手改进”:改个参数名、调个默认值、换个事件线名。这些在语法层面看起来无害,但会破坏 JS 调用方的契约。Skill 把“契约保持”变成了一个不可跳过的前置步骤。
它的门槛在于版本。2.0 宏需要 expo ≥ 57.0.7,而且在 SDK 57 中宏仍然是实验性、无文档状态,官方 beta 预计在 SDK 58[citation:15]。这意味着如果你现在使用它,需要接受 API 可能变化的现实。
对于正在维护 Swift Expo 模块、计划采用 2.0 宏 API 的开发者,这个 Skill 提供了一套结构化的迁移流程。它不会让迁移变得“更快”,但会让迁移变得“更不容易出错”。在契约保持比速度更重要的场景下,这种纪律是有价值的。

◯ 评论 0