React Native 项目初始化:Expo 是“开箱即用”,还是 CLI 才是真高效?
你有没有经历过这样的场景?
刚想动手写一个 React Native 应用,结果卡在环境配置上整整两天——Xcode 版本不兼容、Android SDK 路径报错、CocoaPods 安装失败……明明只是想做个简单的跨平台 App,怎么比开发原生应用还复杂?
这正是React Native 搭建环境长期以来的痛点。而今天,开发者有两个主流选择来绕过这些坑:Expo和React Native CLI。
但问题来了:
- 是该选那个号称“5分钟启动”的 Expo,享受零原生配置的丝滑体验?
- 还是坚持使用 CLI,亲手掌控每一个.gradle文件和Podfile,换来无与伦比的自由度?
这不是简单的工具之争,而是两种开发哲学的碰撞:轻量 vs 高效,便捷 vs 控制。我们得从底层机制说起,才能真正看懂哪条路更适合你的项目。
Expo:把原生世界“藏起来”的聪明人
它到底做了什么?
你可以把 Expo 看作是一个“React Native 的增强版运行时”。它没有取代 React Native,而是给它套上了一层精心设计的外壳。
当你运行:
npx create-react-native-app MyApp --template你就已经站在了 Expo 的肩膀上。这个命令创建的不是一个标准的 RN 项目,而是一个托管工作流(Managed Workflow)项目——所有原生代码都被封装在云端或预构建的容器里。
最直观的表现就是:你根本看不到ios/和android/目录。整个项目看起来就像个纯 JS 工程。
核心机制:Expo Go + SDK + EAS
1. Expo Go:真机调试神器
Expo 提供了一个叫Expo Go的 App,安装在手机上后,扫码就能实时预览你的项目。
这意味着你不需要每次修改都重新编译 APK 或 IPA。热重载(HMR)直接作用于真机,调试效率拉满。
小贴士:Expo Go 本质是一个内置了 React Native 引擎和 Expo SDK 的壳应用。你的 JS 代码通过 Metro 打包后,在这个壳里运行。
2. Expo SDK:50+ 原生功能一键调用
像相机、地理位置、推送通知这些常见功能,Expo 都提供了高质量封装模块。比如:
import { Camera } from 'expo-camera'; import * as Location from 'expo-location'; import { Notifications } from 'expo-notifications';这些不是普通的 npm 包,它们背后绑定了真正的原生实现。更重要的是——无需手动链接。
还记得以前用 CLI 接入react-native-camera时要执行pod install吗?Expo 告诉你:那都是过去的事了。
3. EAS Build:告别本地编译噩梦
发布时怎么办?总不能让用户也装 Expo Go 吧?
Expo 推出了EAS(Expo Application Services)Build,一个基于云的构建系统。你只需要一条命令:
eas build -p androidEAS 就会在云端为你生成可以上架的应用商店安装包(APK/AAB/IPA),完全避开本地 Xcode 和 Android Studio 的版本地狱。
而且支持自定义 native config(通过app.config.js或插件),灵活性远超早期的纯托管模式。
CLI:赤手空拳进入原生战场
它为什么依然不可替代?
如果你用过以下命令:
npx react-native init MyApp那你接触的就是原始、纯粹的 React Native 项目结构。
它的最大特征是什么?
——打开项目,一眼就能看到ios/和android/两个文件夹。
这意味着什么?意味着你拥有了对 iOS 和 Android 工程的完全控制权。
想改启动页?可以。
想加混淆?可以。
想集成微信支付 SDK?完全可以。
想做混合开发,把 RN 页面嵌入现有原生 App?这是它的主场。
CLI 不提供任何“魔法”,但它给你全部权力。
架构真相:JS 与 Native 的桥梁
React Native 的核心原理是“桥接”(Bridge)。JavaScript 运行在独立线程(JSC 或 Hermes),通过序列化消息与原生模块通信。
CLI 项目让你可以直接操作这座桥的两端:
- 在 Android 上,你可以编辑
MainApplication.java添加新的 Package; - 在 iOS 上,你可以修改
AppDelegate.m注册自定义模块; - 甚至可以直接写 Native Module,暴露方法给 JS 调用。
这种能力,在企业级开发中至关重要。
关键差异不在表面,而在“自由度成本比”
很多人误以为“Expo 更简单,CLI 更强大”,于是得出结论:“先用 Expo,不够用了再 eject”。
但现实没这么理想。
让我们拆开来看几个关键维度
| 维度 | Expo(托管工作流) | React Native CLI |
|---|---|---|
| 初始搭建时间 | ⭐⭐⭐⭐⭐(<10分钟) | ⭐⭐(30分钟~数小时) |
| 原生模块支持 | ⭐⭐(需插件或 eject) | ⭐⭐⭐⭐⭐(原生支持) |
| OTA 更新能力 | ⭐⭐⭐⭐⭐(EAS Update 内置) | ⭐⭐(需引入 CodePush) |
| 构建隐私性 | ⭐⭐(依赖云端构建) | ⭐⭐⭐⭐⭐(全程本地可控) |
| 学习曲线 | ⭐⭐⭐⭐(适合新手) | ⭐⭐(需懂原生基础) |
| 性能上限 | ⭐⭐⭐(受限于桥接) | ⭐⭐⭐⭐⭐(可优化至接近原生) |
你看,每个优势背后都有代价。
比如 EAS Build 很方便,但金融类 App 可能不允许代码上传到第三方服务器;
比如 OTA 更新很酷,但苹果曾因“动态下发代码”驳回过类似机制的应用(虽然后来澄清合规即可)。
实战对比:同一个功能,两种写法
我们以“获取设备位置”为例,看看两者实际开发体验的差别。
Expo 方式:三步搞定
import * as Location from 'expo-location'; export default async function getCurrentLocation() { let { status } = await Location.requestForegroundPermissionsAsync(); if (status !== 'granted') return null; return await Location.getCurrentPositionAsync(); }✅ 无需配置权限
✅ 无需安装额外依赖(只要项目是 Expo 的)
✅ 真机扫码立即测试
干净利落,适合 MVP 快速验证。
CLI 方式:步步为营
import Geolocation from '@react-native-community/geolocation'; Geolocation.getCurrentPosition( (pos) => console.log(pos.coords), (err) => console.warn(err), { enableHighAccuracy: true } );但这还不够!你还得:
1. 安装依赖
npm install @react-native-community/geolocation cd ios && pod install2. 添加权限声明
AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />Info.plist (iOS)
<key>NSLocationWhenInUseUsageDescription</key> <string>需要定位权限来提供附近服务</string>3. 可能还要处理自动链接失败的情况(尤其老版本 RN)
你会发现,同样的功能,CLI 多出了至少 5 个操作步骤。
但反过来想,这也意味着你可以精确控制每一项配置——比如只在特定机型开启高精度定位,或者根据用户行为动态申请权限。
如何选择?别被“非此即彼”误导
真正的答案往往是:没有绝对的好坏,只有是否匹配当前阶段的需求。
推荐这样决策:
✅ 优先考虑 Expo 如果:
- 你是个人开发者,想快速做出一款工具类 App(如记账、打卡、备忘录)
- 团队缺乏 iOS/Android 开发人员
- 项目处于 MVP 验证阶段,重点是快速迭代功能
- 需要频繁发布热更新补丁(如活动页面、运营弹窗)
- 不涉及敏感硬件或私有 SDK
典型案例:Taro、Notion 的早期原型、很多创业公司的第一版产品
✅ 优先考虑 CLI 如果:
- 必须接入非公开的原生 SDK(如银行指纹认证、工业蓝牙设备)
- 已有成熟的原生 App,计划逐步迁移到 RN
- 对启动速度、内存占用有硬性指标(比如 <1.5s 冷启动)
- 需要深度定制构建流程(多渠道包、资源混淆、签名策略)
- 所属行业对数据安全要求极高(医疗、金融、政务)
典型案例:Facebook、Shopify、Bloomberg 等大型应用
中间路线:Expo Bare Workflow —— 我全都要?
Expo 早就意识到“托管 vs 自由”的矛盾,于是推出了Bare Workflow。
简单说,就是:保留 Expo SDK 的便利性,同时开放原生工程目录。
你可以:
- 使用
npx create-react-native-app --template bare-minimum创建带ios/和android/的项目 - 继续使用
expo-camera、expo-location等插件 - 同时也能手动集成其他原生库
更进一步,Expo 支持Config Plugins,允许你在app.json中声明需要注入的原生配置:
{ "expo": { "plugins": [ "expo-camera", [ "expo-build-properties", { "android": { "kotlinVersion": "1.8.0" } } ] ] } }这些插件会在构建时自动修改build.gradle、AndroidManifest.xml等文件,相当于把“手动配置”变成了“声明式配置”。
这可能是目前最理想的折中方案:前期快速开发,后期无缝升级为 fully native 项目。
最后一点思考:未来的边界正在模糊
React Native 正在经历一场架构革命:New Architecture(Fabric Renderer + TurboModules + JSI)。
在这个新体系下:
- JS 与 Native 的通信不再走 Bridge,而是通过 JSI(JavaScript Interface)直连;
- 模块可以实现“同步调用”,性能飞跃;
- Expo 已宣布全面支持 New Architecture,并可通过 EAS 构建启用了 TurboModules 的项目。
这意味着什么?
意味着未来你可能既能在 Expo 中使用高性能原生模块,又能享受 OTA 更新和云构建的便利。
换句话说:曾经必须“二选一”的局面,正在被技术进步打破。
但现在呢?现在你还是要面对现实的选择。
如果你问我:“我现在该用哪个?”
我会反问三个问题:
这个 App 是否必须马上上线验证想法?
→ 是 → 选 Expo会不会用到特殊的硬件或私有 SDK?
→ 是 → 选 CLI团队里有人熟悉原生开发吗?
→ 否 → 先用 Expo,留好退出路径
有时候,“快”本身就是一种竞争力。
有时候,“稳”才是长期主义的基石。
而所谓技术选型,不过是在“当下急需”和“未来可期”之间,找到那个刚刚好的平衡点。