RN 与 Expo
环境、Expo 与 tradeoffs。
RN 与 Expo:环境搭建与第一个 App
这一课你将能做到
- 说清楚 React Native 的渲染原理,以及它和 WebView 套壳、Flutter 的本质区别
- 从零搭好开发环境,用 Expo 创建项目并在自己的手机上跑起来
- 熟练使用热重载、调试菜单、真机调试的完整开发循环
为什么要学
你已经会 React,但 React 只能产出网页。公司说「我们要做个 App」,难道再去学一遍 Swift 和 Kotlin?那至少要半年。React Native(下称 RN)让你用 JavaScript + React 的写法,一套代码同时产出 iOS 和 Android 两个原生应用。
人话:RN 就像一名同声传译——你写 <View>,它在 iOS 上翻译成 UIView,在 Android 上翻译成 android.view.View。你看到的不是网页,是真原生控件,所以流畅度和纯原生 App 几乎一样。美团、Instagram、Discord、Shopify 的核心页面都在用 RN,这不是玩具技术。
而 Expo 是 RN 官方推荐的框架:它把相机、定位、推送这些原生能力封装成 JS API,把构建签名扔到云端,让初学者不碰 Xcode、不碰 Android Studio 也能交付 App。这一课的目标只有一个:今天就在自己手机上看到自己写的 App。
先建立图像
import { View, Text } from 'react-native';
export default function App() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 24 }}>你好,React Native</Text>
</View>
);
}
手机上渲染结果:
┌───────────────────────┐
│ │
│ 你好,React Native │ ← 屏幕正中央一行原生文字
│ │
└───────────────────────┘
注意三件事:没有 <div> 没有 <p>;文字必须包在 <Text> 里;样式是 JS 对象不是 CSS 文件。这三条会贯穿整个系列。
RN 的运行架构(新架构):
你的 JS 代码(React 组件树)
↓ JSI(直接内存互调,不再是异步桥)
原生线程:创建 UIView / android.view.View
↓
手机屏幕:真原生控件,手势、滚动都在原生线程
跟着做
步骤 1:准备环境(只要三样东西)
- Node.js 18+:去 nodejs.org 下载 LTS 版,装完终端跑
node -v 验证
- 手机装 Expo Go:iPhone 在 App Store、安卓在各大应用市场搜「Expo Go」安装
- 电脑和手机连同一个 Wi-Fi
不需要 Xcode,不需要 Android Studio,不需要配 Java 环境——这就是选 Expo 的理由,省下的几小时用来写代码。
步骤 2:创建项目
npx create-expo-app@latest my-first-app --template blank
cd my-first-app
npx expo start
npx expo start 启动的是 Metro 打包器(RN 的「webpack」),它会实时把 JS 代码打包发给手机。终端里会出现一个二维码和几个提示按键。
步骤 3:真机运行
- iPhone:用系统相机扫二维码,自动跳转到 Expo Go 打开
- Android:打开 Expo Go,点「Scan QR code」扫码
等几秒(首次要下载 JS bundle),手机上出现「Open up App.js to start working on your app!」——你的第一个 App 已经跑在你自己手机上了。这一步的成就感很重要,它证明整条链路通了。
步骤 4:体验热重载(Fast Refresh)
<Text>Open up App.js —— 我会写 App 了!</Text>
保存文件,手机上的内容 1 秒内自动更新,不用重新编译、不用重装。这就是 RN 开发的核心爽点:改代码即所见。组件的 state 在热重载后还会保留(比如输入框里已打的字不会丢),这叫 Fast Refresh。
步骤 5:认识项目结构
my-first-app/
├── App.js # 应用入口组件,一切从这里开始
├── app.json # Expo 配置:应用名、图标、启动屏、权限声明
├── package.json # 依赖清单,和 Web 项目一样
├── assets/ # 图片、字体等静态资源
│ ├── icon.png # 应用图标
│ └── splash-icon.png # 启动屏图标
└── node_modules/
app.json 长这样,以后改应用名、配权限都在这里:
{
"expo": {
"name": "my-first-app",
"slug": "my-first-app",
"version": "1.0.0",
"ios": { "bundleIdentifier": "com.example.myfirstapp" },
"android": { "package": "com.example.myfirstapp" }
}
}
步骤 6:写一个真正的界面
import { View, Text, StyleSheet } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>我的第一个 App</Text>
<Text style={styles.subtitle}>由 React Native + Expo 驱动</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1, // 占满整个屏幕
justifyContent: 'center', // 主轴(纵向)居中
alignItems: 'center', // 交叉轴(横向)居中
backgroundColor: '#f0f4f8',
},
title: { fontSize: 28, fontWeight: 'bold', color: '#1a1a2e' },
subtitle: { fontSize: 15, color: '#666', marginTop: 10 },
});
保存,手机秒变。注意样式属性全是驼峰:backgroundColor 不是 background-color,数字不带单位(RN 用密度无关像素 dp,自动适配各种屏幕分辨率)。
步骤 7:打开调试菜单
- 摇一摇手机(真机)或终端里按
m:弹出开发者菜单
- 终端按
j:打开 Chrome DevTools 调试器,可以打断点、看 console
- 终端按
r:手动重载整个 App
- 终端按
c:清缓存重启 Metro(玄学问题先按这个)
讲清楚要点
- RN 不是 WebView:WebView 套壳是用浏览器内核渲染 HTML,性能有天花板;RN 把 JS 组件树映射成原生控件渲染,滚动、手势都在原生线程。判断一个 App 是不是 RN 的笨办法:RN 的列表滚动质感和原生一模一样。
- JSI 取代了旧桥(Bridge):老架构里 JS 和原生通信要序列化成 JSON 异步传递,是性能瓶颈;新架构(0.68 起逐步推广)用 JSI 让 JS 直接持有原生对象的引用,同步互调。Expo 新 SDK 默认走新架构。
- Expo 是框架不是玩具:它提供三层东西——统一的 JS API(expo-camera、expo-location…)、云端构建(EAS)、OTA 热更新。第 12 课发布时你会完整用到。
- Expo Go 只是开发沙盒:它是预装了一堆原生模块的「容器 App」,你的代码在它里面跑。正式发布会用 EAS 构建出独立的 .apk / .ipa,那时不再需要 Expo Go。
- 文字铁律:任何文字必须包在
<Text> 里,直接写进 <View> 会直接红屏报错。这是从 Web 转过来的人第一课必踩的坑。
你可能会懵的地方
-
Expo 和纯 RN(Bare Workflow)到底怎么选?
初学者一律选 Expo。90% 的业务需求 Expo 都能覆盖;真遇到某个冷门原生库 Expo 没有,也可以 npx expo prebuild 生成原生工程退回 Bare 模式,代码不用重写。反过来从 Bare 迁 Expo 就麻烦得多。
-
RN 写的 App 会不会一看就是「网页味」?
控件本身是原生的,没有网页味。但如果你不处理平台差异——iOS 的右滑返回、Android 的物理返回键、两端不同的导航栏高度——体验就会露馅。这是第 4 课专门讲的内容。
-
性能真的能和原生比吗?
业务界面(列表、表单、信息流)完全没问题,瓶颈通常在你的 JS 代码写法而不是 RN 本身。真正需要纯原生的是 3D 游戏、实时视频特效这类极限场景。第 11 课专门讲性能优化。
-
为什么不直接用 Flutter?
你已经会 React,RN 的学习成本几乎为零,生态招聘也更友好;Flutter 要学 Dart 和一套全新组件体系,它的优势是渲染完全自绘、跨端像素级一致。选型看团队技术栈,不是看跑分。
-
电脑上能用模拟器吗?
可以。装了 Android Studio 后 npx expo start 按 a 启动安卓模拟器;Mac 上按 i 启动 iOS 模拟器。但初学阶段真机 + Expo Go 是最省事的路径,建议先跑通再说。
错法对照
| 错法 | 问题 | 正解 |
|---|
<View>你好</View> 直接写字 | 红屏:Text strings must be rendered within a Text component | <View><Text>你好</Text></View> |
用 <div> <span> <p> | 组件不存在,直接报错 | 全部换成 View / Text |
样式写 background-color | JS 对象属性不能有连字符 | 驼峰:backgroundColor |
| 电脑手机不在同一 Wi-Fi | 扫码后一直转圈连不上 | 连同一网络,或 npx expo start --tunnel 走隧道 |
| 一上来就装 Android Studio 配环境 | 几小时耗在环境上,热情耗尽 | 先用 Expo Go 在真机跑通,模拟器是后话 |
以为 npx expo start 是在构建 App | 它只是开发服务器 | 正式出安装包是第 12 课的 EAS Build |
| 改完代码手机没反应就重装依赖 | 99% 是缓存问题 | 终端按 r 重载,不行再 npx expo start -c |
卡点急救
npx create-expo-app 卡住或装依赖失败:换国内镜像 npm config set registry https://registry.npmmirror.com,删掉 node_modules 和 package-lock.json 重装。
- 扫码后一直加载转圈:依次排查——手机和电脑是否同 Wi-Fi、电脑防火墙是否拦了 8081 端口、公司/校园网是否有设备隔离(有就用隧道模式
npx expo start --tunnel)。
- Expo Go 提示「SDK version 不兼容」:项目 Expo SDK 版本比 Expo Go 新或旧,升级项目:
npx expo install expo@latest 后 npx expo install --fix 对齐依赖。
- 红屏报错看不动:红屏信息分两段,上半段是错误描述,下半段是调用栈(component stack),先看第一行的错误描述,十有八九能直接定位。
- Metro 端口被占:
npx expo start --port 8082 换个端口。
面试常问
-
RN 的渲染原理?
JS 线程跑 React 代码, reconciliation 后得到组件树,通过 JSI(旧架构是异步 JSON 桥)通知原生线程创建对应的原生视图。UI 渲染、滚动、手势响应全在原生线程执行,所以体验接近原生;JS 线程卡住时界面仍可滚动,但交互回调会延迟。
-
RN 和 H5 混合开发(WebView)的区别?
WebView 是浏览器内核渲染 HTML/CSS,滚动和动画性能有天花板,DOM 操作昂贵;RN 渲染的是原生控件,性能好且体验原生。H5 的优势是发版免审核、一套代码还能跑浏览器;RN 的优势是体验和代码复用率。很多大 App 是「RN 做核心页 + WebView 做运营活动页」的混合架构。
-
Expo 到底解决了什么问题?
三件事:抹平原生环境配置(不用 Xcode/Android Studio 就能开发)、统一常用原生 API(相机、定位、推送、存储都有官方封装)、云端化构建发布(EAS Build / Submit / Update)。它让前端工程师零原生知识也能独立完成 App 从开发到上架的全流程。
-
RN 的优缺点各是什么?
优点:一套代码两端跑、热重载开发效率高、React 生态复用、OTA 热更新能力强。缺点:极端性能场景不如纯原生、第三方原生库质量参差、大版本升级偶有破坏性变更。
自测三问
- RN 渲染出来的控件是原生的还是 WebView 的?这个区别为什么重要?
- 开发阶段用什么在手机上预览 Expo 项目?正式发布时又靠什么构建安装包?
- RN 中文字必须放在哪个组件里?样式属性命名和 CSS 有什么不同?
小结一句话
React Native 让你用已会的 React 写出真原生 App,Expo 把环境和原生能力的门槛降到零——今天先在自己手机上跑起来,后面 11 课把每个能力补全。
延伸