React Native新手入门:Expo 和 CLI 到底怎么选?
你是不是也曾在准备开始写第一个 React Native 应用时,卡在了第一步——到底该用 Expo 还是 React Native CLI?
别急,这几乎是每个初学者都会遇到的“灵魂拷问”。网上说法五花八门:“Expo 简单但限制多”、“CLI 强大但配置太难”,听得人一头雾水。更别说还要面对各种环境报错、SDK 下载失败、Xcode 编译崩溃……还没写代码,热情就被磨光了。
今天我们就来抛开术语堆砌和营销话术,从一个真实开发者的视角,带你把这两个选项彻底理清楚。不讲虚的,只说实战中你会遇到的问题、权衡点,以及我建议你怎么走这条学习路径。
为什么“搭建环境”成了第一道坎?
我们先承认一个事实:React Native 的门槛不在 JS 语法,而在“桥”的那一头——原生平台。
React Native 本质是一个“中间层”:你的 JavaScript 代码运行在一个 WebView-like 的容器里,通过Bridge调用真正的原生 API(比如相机、GPS、蓝牙)。这意味着你要么自己搭桥,要么让别人帮你搭好。
- CLI 就是你自己动手修桥:每一步都可控,但得懂水泥钢筋(Gradle、CocoaPods、权限配置)。
- Expo 是别人已经建好了桥,还铺了栏杆和路灯:你可以直接过河,但不能随便拆改结构。
所以,“选型”本质上是在回答一个问题:
我愿意为开发效率牺牲多少控制权?又是否准备好为自由付出时间成本?
Expo:让你5分钟跑起第一个App
它到底做了什么?
简单说,Expo = React Native + 预打包原生模块 + 云端构建服务 + 开发工具链整合。
它不是替代 React Native,而是给它套了一层“保护壳”。这个壳屏蔽了绝大多数原生配置的复杂性。
举个例子:你想调用手机摄像头。
- 在 CLI 中:你需要安装
react-native-camera→ 手动链接原生库 → 修改 AndroidManifest.xml 和 Info.plist 添加权限 → 处理不同版本兼容问题。 - 在 Expo 中:装个包,导入组件,搞定。
npx create-expo-app MyCameraApp cd MyCameraApp npm install expo-camera然后直接写:
import { Camera } from 'expo-camera'; import { useEffect, useState } from 'react'; import { View, Text } from 'react-native'; export default function App() { const [permission, requestPermission] = Camera.useCameraPermissions(); useEffect(() => { requestPermission(); }, []); if (!permission?.granted) { return <View><Text>需要授权才能使用相机</Text></View>; } return ( <Camera style={{ flex: 1 }} type="back" /> ); }运行:
npx expo start扫码,立刻看到画面。整个过程不需要 Android Studio 或 Xcode。
这就是Expo 的核心价值:把“能不能跑起来”和“怎么实现功能”解耦。你可以先专注逻辑验证,再考虑工程细节。
适合谁用?
✅产品原型快速验证
老板说“做个能扫二维码的App看看市场反应”,你两天就能拿出可演示版本。
✅教学培训场景
教学生写移动端应用时,没人想第一天就卡在pod install报错上。
✅轻量级内部工具
企业内部的审批表单、数据看板、员工打卡这类应用,基本不涉及复杂原生交互。
✅前端转 RN 的过渡期
你熟悉 React,想先理解 RN 的工作流,而不是一上来就被 Gradle 吓退。
它有哪些“看不见的代价”?
别误会,天下没有免费午餐。Expo 的便利是有边界的。
1. 包体积偏大(首屏约20MB+)
因为 Expo SDK 把很多可能用不到的原生模块都打进了基础包里。虽然现在支持 App Slicing(按需加载),但初始体积仍比纯 CLI 项目大不少。
🎯 提示:如果你做的是面向海外用户的 App,这点影响不大;但在网络条件差或存储紧张的市场(如印度、东南亚部分机型),就得慎重。
2. 原生模块支持有限
尽管 Expo SDK 提供了超过 40 个常用 API(相机、文件系统、传感器、推送等),但一旦遇到小众需求,比如:
- 接入特定厂商的蓝牙协议
- 使用 ARKit/ARCore 深度定制
- 集成银行级指纹认证 SDK
你就只能两条路:
- 放弃 Expo,执行eject
- 使用Expo Dev Client自定义构建(相当于半脱离)
而eject是不可逆操作——一旦脱离 Expo “保护罩”,后续所有构建、调试都要你自己负责。
3. 构建依赖 EAS(Expo Application Services)
发布到应用商店需要使用 EAS Build,这是 Expo 的云端编译服务。虽然免费额度够用,但它意味着:
- 你失去了本地构建能力
- 构建速度受网络和队列影响
- 某些行业(如金融、医疗)因合规要求禁止代码上传第三方服务器
React Native CLI:完全掌控,但也得自己扛所有风险
它给了你什么?
一句话总结:你拥有整艘船的所有舱室钥匙。
创建项目后,你会看到完整的目录结构:
MyCLIApp/ ├── android/ │ ├── app/ │ └── build.gradle ├── ios/ │ ├── Podfile │ └── MyCLIApp.xcworkspace ├── src/ └── package.json这意味着你可以:
- 直接修改原生启动页(SplashScreen)
- 手动集成任何.aar或.framework文件
- 控制每一个权限声明、后台任务策略、内存回收机制
- 使用 Flipper 进行深度性能分析
- 自由选择 CI/CD 流程(Fastlane + GitHub Actions / GitLab CI)
例如,你要集成 Firebase Analytics:
// ios/MyCLIApp/AppDelegate.m #import <Firebase.h> - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [FIRApp configure]; // 其他初始化逻辑 return [super application:application didFinishLaunchingWithOptions:launchOptions]; }这种级别的介入,在标准 Expo 项目中是不可能的(除非 eject)。
什么时候必须上 CLI?
🚨广告 SDK 集成
AdMob、穿山甲、Meta Audience Network 等主流广告平台都需要原生依赖。Expo 社区虽有封装,但更新慢、功能残缺,上线级项目基本都得走 CLI。
🚨IoT 设备通信(BLE、NFC)
虽然 Expo 提供了expo-bluetooth,但仅支持基础扫描与连接。若需自定义 GATT profile、处理长连接心跳、低功耗优化,就必须写原生代码。
🚨高性能动画或游戏引擎
涉及到 Canvas 渲染、WebGL、物理模拟等功能,往往需要接入原生图形库(如 Skia、OpenGL),CLI 是唯一选择。
🚨已有原生资产复用
公司已有成熟的原生模块(如加密算法、图像处理库),希望用 RN 做 UI 层包装,这时 CLI 更容易桥接。
新手真的能驾驭 CLI 吗?
坦率说,很难。
我见过太多新手被这些问题劝退:
-Could not determine java version—— JDK 版本不对
-pod install failed—— Ruby 环境乱、CocoaPods 源慢
-No connected devices found—— ADB 驱动没装好
-Metro can't resolve module—— 缓存没清干净
这些都不是代码问题,而是环境工程问题。解决它们需要查日志、翻 GitHub Issues、懂一点操作系统知识。
💡 建议:如果你想走这条路,请确保你有至少半天时间专心配置,且有一台性能尚可的电脑(尤其是 macOS,iOS 开发绕不开)。
对比一张表,看清本质区别
| 维度 | Expo | React Native CLI |
|---|---|---|
| 初始化时间 | ⏱️ 5分钟内 | ⏳ 30分钟~数小时 |
| 是否需要 Xcode/Android Studio | ❌ 不需要(开发阶段) | ✅ 必须 |
| 原生模块扩展能力 | ⚠️ 受限(需 eject 或 Dev Client) | ✅ 完全自由 |
| 构建方式 | ☁️ 云端 EAS Build(推荐) | 💻 本地构建 |
| 调试体验 | 📱 Expo Go App 实时预览 | 🔍 Flipper + Chrome DevTools |
| 包体积(初始) | ~20MB | ~8–10MB |
| OTA 更新支持 | ✅ 支持 JS 热更新 | ❌ 需自行实现 |
| 学习曲线 | 平缓,适合新手 | 陡峭,需了解原生生态 |
我该怎么选?三个决策建议
✅ 建议一:90% 的新手,从 Expo 起步
别一上来就想“我要做商业级项目”,先让自己跑通闭环。
用 Expo 完成你的第一个登录页、列表页、拍照上传功能。在这个过程中你会学到:
- RN 的组件模型
- 样式系统(Flexbox)
- 导航器(React Navigation)
- 状态管理(Redux/Zustand)
- 网络请求(Axios)
这些才是移动开发的核心。等到你真正遇到“Expo 不够用”的那天,你已经有能力判断该如何迁移。
🧭 类比:学开车先从自动挡开始,别一上来就练坡道起步+离合控制。
✅ 建议二:警惕“eject”的诱惑
很多人觉得:“我现在用 Expo,以后不行再 eject 就好了。”
但现实是:eject 是一次重大架构升级,不是一键切换。
一旦 eject,你就得立刻接手所有原生构建流程。相当于突然被扔进深水区游泳。
更好的做法是:
1. 保持 Expo 项目
2. 当需要某个原生功能时,评估是否有 Expo 兼容方案
3. 如果没有,尝试使用Expo Dev Client
4. 只有当 Dev Client 也无法满足时,才考虑 eject
Dev Client 允许你在保留 Expo 工具链的同时,添加自定义原生代码,是一种平滑过渡方案。
✅ 建议三:根据项目类型做决定
| 项目类型 | 推荐方案 | 理由 |
|---|---|---|
| MVP / 产品验证 | Expo | 快速迭代,支持 OTA 试错 |
| 教学 Demo | Expo | 减少学员环境干扰 |
| 电商类 App(含广告、支付) | CLI | 第三方 SDK 集成刚需 |
| IoT 控制面板 | CLI 为主 | BLE 操作深度依赖原生 |
| 内部管理系统 | Expo | 功能集中于 CRUD,无需复杂原生交互 |
最后一点思考:工具会变,理解底层更重要
Expo 正在变得越来越强大。它的 Dev Client 模式正在模糊与 CLI 的界限。未来甚至可能出现“全程无需 eject”的高度定制化开发模式。
但无论工具如何进化,有几点永远不会变:
- JS 与原生之间的 Bridge 机制
- 生命周期管理(Activity / ViewController)
- 内存泄漏排查思路
- 构建流程的本质(编译 → 打包 → 签名)
所以,最好的策略不是纠结选哪个工具,而是利用合适的工具,尽快触达这些底层知识。
当你能在 Expo 上写出流畅的 App,也能看懂 CLI 项目的MainApplication.java,你就不再“被工具选择”,而是能主动“选择工具”。
如果你刚开始学 React Native,不妨试试这样一条路径:
- 第1周:用 Expo 搭建项目,完成一个带 Tab 导航的小应用
- 第2周:引入 Redux 管理状态,加个拍照上传功能
- 第3周:尝试接入一个非 Expo 支持的库(如地图 SDK),研究如何用 Dev Client 解决
- 第4周:创建一个 CLI 项目,对比两者差异,亲手走一遍
run-android
一个月下来,你会发现自己已经跨过了最艰难的入门期。
你现在打算用哪个方式开始?如果在实践中有任何坑,欢迎留言讨论。我们一起踩过去。