Expo 仓库架构全景解读:Expo SDK、Modules API、CLI 与 EAS 的 Monorepo 组织方式
本文基于 Expo 仓库根目录的 README 及其配套源码,系统讲解 Expo 这个"用 React 构建 Android、iOS 与 Web 通用原生应用"的开源平台的仓库结构、SDK 组成、测试应用矩阵与 Monorepo 工程化体系。读完后,你能快速定位仓库中任意组件(SDK 模块、CLI、模板、文档)的源码位置,理解各目录的职责边界,并掌握在本地构建、测试与贡献这套代码库的标准流程。
Expo 是什么
README 对 Expo 的定义是:一个构建通用原生应用的开源平台,应用可运行在 Android、iOS 和 Web 上;它提供一套通用运行时(universal runtime)和一系列库,让开发者只写 React 和 JavaScript 就能构建原生应用。
这个仓库(root 包名 @expo/expo,见 package.json)包含的内容包括:
- Expo SDK:即 packages/expo 及其下的全部
expo-*模块,当前仓库内 SDK 版本为57.0.9(见 packages/expo/package.json); - Modules API:用于编写自定义原生模块的基础设施,核心在 packages/expo-modules-core;
- Expo Go 应用:即仓库内置的官方客户端,源码位于 apps/expo-go;
- CLI:
expo命令行工具,源码位于 packages/@expo/cli; - Router:声明式路由方案,位于 packages/expo-router;
- 文档与配套工具:官方文档站源码在 docs,构建/配置工具在 tools。
此外,README 指出 Expo Application Services (EAS) 是一组与 Expo 开源工具深度集成的托管服务(构建、发布、迭代),面向个人与团队。注意 EAS 的服务端不在本仓库中,仓库内主要是与之配套的开源工具链。
仓库目录布局详解
README 的 "Project Layout" 一节是理解整个仓库的地图。下表在原文档基础上,结合仓库实际内容做了扩充:
| 目录 | 职责 | 补充说明 |
|---|---|---|
| packages | 所有 Expo 模块的源码 | 想修改某个库或研究其实现原理,从这里入手。expo-constants、expo-image、expo-router 等近百个包都在这里 |
| apps | 链接到开发中模块的 Expo 工程 | 日常开发测试主要在这里进行,apps/README.md 明确说明不要向本仓库新增 app,每个 app 都会增加维护成本 |
| apps/expo-go | Expo Go 客户端源码 | 开发 iOS 时必须打开 ios/Exponent.xcworkspace 而不是 Exponent.xcodeproj,因为 workspace 会同时加载 CocoaPods 依赖 |
| docs | https://docs.expo.dev 官网源码 | 基于 Next.js 构建,docs/README.md 描述了版本化文档的工作流 |
| templates | npx create-expo-app 使用的模板 |
包含 blank、blank-typescript、default、tabs、bare-minimum 五种模板 |
| react-native-lab | 用于构建 Expo Go 的 React Native fork | README 称其为 "our fork of react-native",与上游仅保持必要的最小分叉 |
| guides | 高级主题的深入教程 | 例如 Expo Module Infrastructure、Generating Jest Mocks 等 |
| tools | 构建与配置工具 | 内含 expotools(et 命令)等内部 CLI 工具链 |
| template-files | 需要私有密钥的文件模板 | 由 template-files/keys.json 中的密钥填充 |
其中 template-files/ios/dependencies.json 专门指定了 bare-minimum 模板应用的 CocoaPods 依赖列表。
packages:SDK 的模块组成
packages/ 是仓库的核心,采用 pnpm workspaces 组织(见 pnpm-workspace.yaml,包匹配 packages/* 与 packages/@expo/*)。从源码结构看,模块大致分为几类:
1. 基础 SDK 包(packages/expo)。这是 "The Expo SDK" 本体,从 packages/expo/package.json 可以确认:
bin字段暴露了三个可执行命令:expo(CLI 入口)、expo-modules-autolinking(模块自动链接)、fingerprint(应用指纹);- 核心依赖包括
@expo/cli、@expo/config、@expo/config-plugins、@expo/metro-config、babel-preset-expo、expo-modules-core等,全部以workspace:协议链接到本仓库内部版本——这正是 "SDK、CLI、Metro 配置统一发版" 的实现方式; - 对
react/react-native采用*的 peerDependencies,保证 SDK 可适配不同 RN 版本。
2. 功能模块(expo-*)。每个模块都遵循统一目录约定,以 packages/expo-constants 为例:
src/:TypeScript 源码,编译输出到build/(不提交 Git,由 Turborepo 按需构建与缓存);ios/、android/:原生侧实现(Swift/Kotlin 及expo-module.config.json声明的模块配置);- 常见 npm 脚本:
build、typecheck、test、lint、format、depscheck,由统一的 packages/expo-module-scripts 编排(CONTRIBUTING.md 中的 "Common package scripts" 一节有完整脚本表)。
3. 基础设施包(packages/@expo/*)。包括 cli(命令行)、config / config-plugins(应用配置解析与配置插件)、metro-config / metro-file-map(打包器定制)、fingerprint(构建指纹)、doctor(诊断工具)等约三十个包。
4. 模块基础设施。packages/expo-modules-core 提供了模块自动链接、JSI 桥接等底层能力;packages/expo-modules-autolinking 负责把 packages/ 中的原生模块自动织入 bare-expo、expo-go 等宿主工程。编写自定义模块的机制在 guides/Expo Module Infrastructure 中有完整说明。
apps:测试与验证应用矩阵
README 指出 "You'll do most of your testing in here"。apps/README.md 列出了仓库内 app 的官方定位:
- bare-expo:裸 React Native 应用,链接
packages/中全部 SDK 依赖,用来运行apps/test-suite与apps/native-component-list的 JS 代码,是 SDK 开发的主力沙盒(CONTRIBUTING.md 明确推荐用bare-expo而非 Expo Go 做 SDK 开发,因为 Expo Go 需要 API token 且配置复杂); - Expo Go(apps/expo-go):官方客户端 UI,包含完整的 Android/iOS 原生工程;
- native-component-list(apps/native-component-list):默认 Expo 预设中全部组件与 API 的展示清单,适合需要手动交互验证的 UI 测试;
- test-suite(apps/test-suite):跑在设备/模拟器上的端到端测试集合,
bare-expo会导入它的根组件作为自身根组件; - sandbox(apps/sandbox):本地实验项目,被排除在 Git 跟踪之外。
除这五个外,仓库还有若干专项工程:eas-expo-go(EAS 构建专用客户端)、minimal-tester、observe-tester、router-e2e(expo-router 的端到端测试)、brownfield-tester(原生工程内集成 Expo 的混合场景测试,对应 packages/expo-brownfield)。
验证原生安装是否成功的标准流程(引自 CONTRIBUTING.md):
cd apps/bare-expo
pnpm ios # iOS 模拟器
pnpm android # Android 模拟器/设备
运行成功后,你就在 bare-expo 中跑起了 test-suite 应用;修改 SDK 包后无需重新打包 JS 部分,Metro 会热更新,但原生代码改动必须重新构建。
模板:create-expo-app 的起点
templates/ 目录保存了 npx create-expo-app 所用的项目模板,与 README 描述一一对应:
- expo-template-blank / expo-template-blank-typescript:最小依赖集 + 空根组件。以 TypeScript 版为例,templates/expo-template-blank-typescript/package.json 只声明
expo、expo-status-bar、react、react-native四个依赖,脚本为标准的expo start --android/--ios/--web; - expo-template-default:默认模板,含示例页面与样式;
- expo-template-tabs:基于 expo-router 的选项卡式应用;
- expo-template-bare-minimum:自带完整原生工程(
android/、ios/目录)的裸应用模板,其原生文件即由上文template-files/中的模板与密钥生成。
工程化体系:pnpm + Turborepo 的 Monorepo
README 只字未提构建体系,但理解仓库无法绕过它。从 package.json 与 turbo.json 可以确认:
- 工作区定义:root
package.json的workspaces与 pnpm-workspace.yaml 共同声明了apps/*、packages/*、packages/@expo/*等范围;pnpm-workspace.yaml还配置了preferWorkspacePackages: true(优先链接工作区内同名包)与一份patchedDependencies补丁清单(对应仓库 patches 目录下的.patch文件,如react-native-reanimated、react-native-safe-area-context等)。 - Turborepo 任务图:turbo.json 定义了
build(依赖^build,按拓扑序构建并缓存build/、plugin/build/等输出)、typecheck、test、lint、format任务,build任务启用了 10GB 本地缓存与远端缓存。因此 root 的pnpm build、pnpm test、pnpm lint会经turbo分发到所有工作区包并复用缓存。 - 环境约束:
engines要求 Node^22.13.0 || ^24.3.0 || ^26.0.0 || >=27.0.0、pnpm^10.33.0;CONTRIBUTING.md 另要求 Ruby 3.3+(iOS 侧)、推荐 JDK 17(Android 侧),且 Windows 需通过 WSL 开发。
Expo Go 徽章
README 提供了一个可直接复制的徽章,用于在你的项目中标识 "可在 Expo Go 中即时运行"。徽章源码为 shields.io 的 Runs with Expo Go 徽章(黑底与蓝底 4630EB 两个配色版本),README 中给出的 Markdown 片段原样保留如下,可粘贴到任何项目 README 中:
[](https://expo.dev/client)
[](https://expo.dev/client)
学习路径与文档入口
README 的 Documentation 一节指向三块官方内容(均托管在 docs.expo.dev,其源码即本仓库 docs 目录):
- Getting Started:从零创建、运行、发布应用的入门路径;
- API Reference:按 SDK 版本归档的模块 API 文档,源码位于 docs/pages/versions(
unversioned/为最新未发布版本,版本发布时由 docs/README.md 描述的工作流同步); - Using Custom Native Modules:自定义原生模块指南,对应仓库内 docs/pages/modules 与 guides/Expo Module Infrastructure。
仓库内 docs 目录本身就是一个 Next.js 工程,包含页面源(docs/pages)、MDX 插件(docs/mdx-plugins)、UI 组件(docs/ui)与文档质量检查脚本(docs/scripts,如链接协议校验、重定向检查等测试用例)。
贡献流程要点
README 指向 CONTRIBUTING.md 作为贡献入口。结合该文件,关键事实如下:
- 接收 PR 的范围:
packages/、docs/、templates/、guides/、apps/与 markdown 文件;通常不接收原生依赖版本号升级类 PR(由团队在发版流程中统一处理)。 - 本地开发循环:
cd packages/<包名>编辑src/→pnpm build编译 → 通过bare-expo在模拟器/真机验证;JS 改动可expo start热更,原生改动需重建。 - 测试三件套:包内 Jest 单元测试(
src/__tests__/,桥接的新原生函数需在 jest-expo 中 mock,见 guides/Generating Jest Mocks)、apps/test-suite中的端到端测试(新增文件需登记到 apps/test-suite/TestUtils.js)、native-component-list中的手动演示用例。 - 提交前检查:
et check-packages <...packages>(等价于 CI 的 Turborepo 任务图);修改文档时需同步docs/pages/versions/unversioned/下的对应版本文档;用户可见变更需记入所改包的CHANGELOG.md或 CHANGELOG.md。 - 提交信息约定:
[platform][api] Title格式,如[ios][video] Fixed black screen bug...。
许可证
按 README 说明:Expo 源码以 MIT 协议发布(见 LICENSE),部分第三方依赖采用其他协议(如 BSD),详见 THIRD-PARTY-LICENSES。
小结
Expo 仓库是一个职责分层清晰的 Monorepo:packages/ 承载 SDK 与全部模块源码,apps/ 承载 Expo Go 与一整套测试沙盒,templates/ 承载开箱即用的项目起点,docs/ 与 guides/ 承载知识沉淀,tools/ 与根部的 pnpm workspaces + Turborepo 配置则把这些部分黏合成一个可增量构建、可缓存、可远端协作的工程体系。若你的目标是使用 Expo 开发应用,从 npx create-expo-app 生成的模板入手;若你的目标是研究或修改 Expo 本身,从 packages/expo 与 apps/bare-expo 这两个入口开始,是效率最高的路径。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00