mcpField
E
expo-web-to-native

expo-web-to-native

github.com/expo开发3.5k20.2k 次安装今天更新
代码仓库

关于 expo-web-to-native

expo-web-to-native 是一项 Expo 技能,为现有 React Web 应用提供渐进式原生迁移路径,并在逐步转换屏幕期间保持可发布状态。它涵盖评估、DOM 外壳、屏幕重设计、数据接入和发布。

主要功能

  1. 01评估并分类屏幕
  2. 02先搭建 DOM 组件外壳
  3. 03逐步将屏幕转换为原生界面
  4. 04通过参考资料指导原生重设计
  5. 05对照 Web 应用验证运行中的屏幕
  6. 06接入数据、身份验证和存储

适用场景

  1. 01规划现有 React 应用的渐进式迁移
  2. 02在原生屏幕开发前发布 Web 外壳
  3. 03确定原生重设计屏幕的优先级
  4. 04对比原生屏幕与 Web 原版的行为

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/expo/skills --skill expo-web-to-native

安装后在 Claude Code 中输入/expo-web-to-native确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
references/
false-friends.md
native-patterns.md
run-as-goal.md
verify-on-device.md

详细介绍

从 Web 迁移到原生

Web React 应用不会转换为原生应用——没有转译器。它会逐个屏幕地迁移,就像绞杀榕围绕一棵树生长并逐渐取代它:搭建一个原生外壳,第一天就在里面运行整个 Web UI,然后按照优先级逐个将屏幕绞杀为原生屏幕。本技能是安排工作顺序的主干;每一步都会交由现有的 Expo 技能处理,而不是重新解释一遍。它将 Expo 的 From Web to Native with React 落到实处——阅读该文了解原因。

flowchart TD
    A1[1 · Assess: write the worklist] --> A2[2 · Scaffold Expo shell]
    A2 --> A3[3 · DOM-component shell<br/>· expo-dom · SHIP DAY ONE]
    A3 --> A4[4 · Strangle screens to native<br/>highest-value first · expo-router]
    A4 -->|more screens| A4
    A4 --> A5[5 · Wire data / auth / storage<br/>· expo-data-fetching]
    A5 --> A6[6 · Ship · eas-app-stores]

原则

  • 迁移,不要重写。 绝不一次性全面替换;每一步都要确保应用可以交付。
  • 第一天就交付。 在任何内容原生化之前,Web UI 就会在 DOM 组件外壳中运行(第 3 步)——这就是里程碑;之后的一切都是优化。
  • 按价值逐步替换。 将高频使用的屏幕原生化,其余部分保留在 WebView 中。每个 DOM 屏幕都会携带约 2 MB 的 Web 运行时——这足以说明不该把所有内容都作为 DOM 来交付。
  • 原生化意味着重新设计,而不是换皮。 被替换的屏幕应该看起来像 Apple/Google 原生推出的界面,而不是套了原生外观的网页。优先使用 @expo/ui——它会渲染真正的 SwiftUI/Compose,因此感觉与操作系统完全一致;仅在需要自定义布局时才将带样式的 RN 基元作为后备方案。此外,还可以使用平台导航(expo-router:NativeTabs、大标题)、通过 @expo/ui 实现的 Liquid Glass 和原生组件,以及移动端 UX(底部弹层、滑动、触觉反馈)。Web→原生模式映射见 ./references/native-patterns.md。如果它看起来仍像网站,那你只是移植了界面,并没有重新设计。
  • 通过运行验证,而不是编译。 构建成功证明不了什么(空白 WebView 也能编译通过)。运行每个屏幕——但要对照 Web 原版评估内容和行为,而不是像素(原生化后的屏幕应该更有原生感,而非完全相同)。
  • 统筹编排,不要重新发明。 每一步都会转交给现有技能。这里的价值在于顺序和注意事项——逐项习惯用法的映射见 ./references/false-friends.md。

按循环方式运行(推荐)

迁移是一个漫长的“重复直到完成”循环,因此第一步是编写目标说明并启动它——而不是手动逐个处理屏幕。在 ./references/run-as-goal.md 中填写此应用的目标说明并呈现出来;它会在每次迭代中重新读取本技能,因此每一轮 /goal 都会重新加载操作指南和工作清单,并推进下一个屏幕(甚至会自行引导 assess 步骤)。然后使用该目标说明运行 /goal——或者,如果运行环境无法循环,就将它写入 migration-goal.md 并让用户启动。下面的步骤就是每次迭代要执行的内容;只有在不使用循环时才手动执行。

迁移流程

没有要迁移的代码仓库——只是作为 Web 开发者从头构建原生应用?你不需要这些步骤:使用 expo-router,并打开 ./references/false-friends.md 作为 Web→原生习惯用法映射参考。以下所有内容都假设已有一个 Web 应用。

1. 评估 → 编写工作清单

阅读代码仓库并生成 migration-progress.md,作为后续迁移检查的持久工作清单。划分两个方面:

  • 屏幕与后端。 页面路由(page.tsx)是要迁移的屏幕;服务端路由(route.ts)、ORM 和身份验证处理程序仍留在服务端。一次性决定后端方案:保持部署状态(原生应用成为 HTTP 客户端),或将其迁移到 EAS Hosting(eas-hosting)。
  • 为每个屏幕分类,确定其迁移方式:原样移植(展示型 → 在 DOM WebView 中运行)、立即原生化(高频使用,或需要原生体验——手势、列表、键盘)、稍后原生化,或混合式(原生外壳包裹 Web 子树,例如,用聊天列表包裹 Markdown 渲染器)。

阅读时记下框架相关信息——RSC 还是客户端、Tailwind/shadcn、数据获取位置——因为这些会影响每个屏幕的迁移方式(false-friends 中有对应关系;尤其是异步服务端组件,必须拆分为客户端获取数据组件和展示组件,才能迁移)。也要标记第三方服务/SDK——浏览器 SDK 无法直接沿用(false-friends → 服务与 SDK);支付尤其需要注意,应分叉处理,而非替换(应用内数字商品必须通过 RevenueCat 使用应用商店 IAP,约 30% 抽成——不能用 Stripe),这是现在就要做出的商业模式决策,而不是等到 App Store 审核时再决定。只有在所有路由都完成分类、所有屏幕都完成归类后,这份工作清单才值得信赖。

2. 搭建外壳

使用 create-expo-app,然后在 Expo Router 中映射 Web 路由——Next 的路由树几乎可以一对一映射(注意:[id]/page.tsx → [id].tsx,路由也可能位于 src/app/)。为每个路由创建一个空屏幕。

3. 使用 DOM 组件搭建外壳——第一天的里程碑

将每个屏幕迁移为 DOM 组件(使用 'use dom',参照 expo-dom 技能),并由其原生路由渲染,这样在进行原生化之前,整个应用就能在手机上运行。预计需要逐屏修改——解除服务端组件封装、替换框架导入(next/link)、沿用样式——这些都由 false-friends 涵盖。然后按下文所述运行并验证;完成后即可直接提交 TestFlight。

4. 按价值将屏幕原生化

从上到下依次处理 migration-progress.md。对每个屏幕,使用原生方式重新设计,不要照搬 Web 布局。优先使用 @expo/ui(真正的 SwiftUI/Compose——按钮、列表、底部弹出层、选择器、滑块;./references/native-patterns.md 对照列出了哪些 Web 模式对应哪些原生组件),然后使用平台导航(expo-router——NativeTabs、大标题)和移动端 UX(滑动操作、触觉反馈、惯性/反向滚动);仅在自定义布局时使用 RN 基础组件。每种惯用模式都可参阅 ./references/false-friends.md。@expo/ui 和 DOM 组件都能在 Expo Go(SDK 56+)中运行——只有在使用自定义原生模块时,才需要开发构建(expo-dev-client 技能)。对照运行中的 Web 原版,验证内容和行为(外观应更具原生感),然后勾选完成。每轮只处理一个屏幕,应用始终可发布。这是一个围绕持久工作清单的循环,因此可以无人值守地运行——将其交给目标循环(./references/run-as-goal.md)。

5. 接入数据、身份验证和存储

Web 数据层无法在迁移后继续使用——相对路径请求、Cookie 会话、localStorage 和环境变量都需要调整(替换方案见 false-friends)。使用 expo-data-fetching 处理请求和缓存;如果后端迁移到了 EAS Hosting,则添加 eas-hosting。

6. 发布

使用 eas-app-stores 构建应用商店版本(App Store / Play / TestFlight),之后使用 EAS Update 推送 OTA 更新。

通过运行验证,而不是编译

expo export 成功只能证明屏幕已打包,不能证明它能正确渲染——屏幕可能构建成功,却仍然显示空白或渲染错误。因此,在完成外壳以及每个原生化屏幕后,都要针对同一路由比较两个正在运行的应用:

  • 原始 Web 应用 — 使用 agent-browser(vercel-labs CLI)截取:打开路由,执行 snapshot --json 获取无障碍树,再执行 screenshot。
  • 原生应用 — 使用 argent 驱动模拟器:用 describe / debugger-component-tree 查看结构,并用 flow 在每轮中重放检查。

通过标准是内容和行为一致,而非像素一致:原生化屏幕应比 Web 更有原生感,而不是与 Web 完全相同(DOM 外壳阶段除外——该阶段呈现的就是 Web UI,因此应保持一致)。原生体验也包含感受,无法通过截图体现——对于带有转场或手势的屏幕,请录制一段短视频,而不只是截取静态画面(参见 native-patterns.md → Feel)。这个流程对所用工具有明确要求:如果没有安装 agent-browser 或 argent,请先询问用户并安装,然后再继续——不要改用手动截图。完整流程和设置方法见 ./references/verify-on-device.md。

参考资料

  • ./references/false-friends.md — Web 习惯用法 → 原生等效方案,以及每种方案的注意事项。用于第 3–5 步的查询,也适合需要改掉 Web 开发习惯的情况。
  • ./references/native-patterns.md — Web UX 模式 → 原生重新设计(优先使用 @expo/ui)。用于第 4 步的重新设计指南,让屏幕具有原生 OS 的体验,而非仅仅换肤。
  • ./references/verify-on-device.md — 双 Agent 的一致性检查流程:驱动 Web 应用(浏览器 Agent)和原生应用(argent),打开同一路由并进行比较。
  • ./references/run-as-goal.md — 现成的、针对迁移任务的目标,用于无人值守地执行第 4 步(每次迭代都会重新读取此技能)。
  • Expo — 使用 React 从 Web 走向原生 — 本技能付诸实践时所依据的权威指南。

提交反馈

如果你发现此技能存在错误、容易误导或已经过时的信息,请提交反馈,帮助 Expo 改进:

npx --yes @expo/agent-cli@latest feedback --category skills --subject "expo-web-to-native" "<actionable feedback>"

只有在有具体且可执行的反馈时才提交。请尽可能提供相关背景信息。 如果 Agent 反复无法完成 Expo 任务,或用户不得不介入,请使用 --category evals 提交简要报告,说明任务、预期结果和实际问题,并将相关 Expo 软件包或命令作为 --subject。

评论

支持 Markdown