要点
开发服务器、应用进程和设备是不同参与者。设备必须能访问打包服务;原生崩溃与 JavaScript 错误也在不同日志中出现。
创建工程
方式一:Expo(推荐)
npx create-expo-app@latest MyFirstApp
cd MyFirstApp
npx expo start
创建完成后按终端提示可尝试打开对应目标(a = Android,i = iOS,w = Web)。npx expo start 启动的是 Metro 开发服务器:首次运行前,先确保目标模拟器/设备和对应 native build 已按环境搭建准备好。模板是否使用 Expo Router、首页在哪个文件,取决于创建时选的模板;先查看生成的项目目录再编辑。
方式二:RN CLI(bare)
npx @react-native-community/cli init MyFirstApp
cd MyFirstApp
1. Android
npm run android
1. iOS(仅 macOS;若仓库提供 Gemfile,使用它固定 CocoaPods)
bundle exec pod install --project-directory=ios
npm run ios
核心概念:Metro 与启动链路
定义: Metro 是 RN 官方的 JavaScript 打包器,角色类似 Web 侧的 webpack/vite。
启动链路(四步):
- 入口注册:
App.tsx(或入口文件)通过AppRegistry.registerComponent注册根组件 - 打包: Metro 根据入口和依赖图转换、解析并提供 JavaScript bundle;是否采用 Hermes、怎样生成生产产物,取决于项目和构建配置。
- 加载: 开发模式下,已安装的原生应用向 Metro 请求 bundle;生产构建使用随安装包或更新通道交付的 bundle。两者的网络路径、缓存和错误日志不同。
- 渲染: 原生宿主加载运行时,把 React Native 组件更新提交为平台原生视图。Fabric 是新架构的一部分,不应把它当成所有项目已经启用的前提。
与 Web React 的关键差异: RN 没有浏览器 DOM,View/Text 组件最终映射为 Android 的 ViewGroup/TextView、iOS 的 UIView/UILabel。写 <div> 会直接报错。
第一个界面
打开 app/(tabs)/index.tsx,替换为以下代码(expo-router 页面组件:导出默认组件即可,无需 AppRegistry;App.tsx 仅见于 blank 模板与 bare 工程):
// app/(tabs)/index.tsx
import { useState } from 'react';
import {
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
Platform,
} from 'react-native';
export default function Home() {
const [name, setName] = useState('');
const [greeting, setGreeting] = useState('还没有提交名字');
function submitName() {
const normalized = name.trim();
setGreeting(normalized ? `欢迎,${normalized}` : '请输入名字后再提交');
}
return (
<View style={styles.container}>
<Text style={styles.title}>
你好,React Native!运行在 {Platform.OS} 上
</Text>
<TextInput
style={styles.input}
placeholder="输入你的名字"
value={name}
onChangeText={setName}
/>
<TouchableOpacity
accessibilityRole="button"
accessibilityLabel="提交名字"
style={styles.button}
onPress={submitName}
>
<Text style={styles.buttonText}>提交</Text>
</TouchableOpacity>
<Text style={styles.result}>{greeting}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 20,
backgroundColor: '#fff',
},
title: {
fontSize: 22,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 24,
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 12,
marginBottom: 16,
},
button: {
backgroundColor: '#007AFF',
borderRadius: 8,
padding: 14,
alignItems: 'center',
},
buttonText: {
color: '#fff',
fontSize: 16,
},
result: {
marginTop: 16,
textAlign: 'center',
},
});
可观察结果:输入 Ada 后点“提交”,页面显示“欢迎,Ada”;清空输入后提交,显示“请输入名字后再提交”。保存后 Metro 会尝试通过 Fast Refresh 更新设备;能否保留组件状态取决于改动是否满足刷新边界。若更新失败或状态不符合预期,按提示完整重新加载再判断代码效果。
真机运行
Android 真机
- 手机开启”开发者选项 → USB 调试”
- USB 连接电脑,
adb devices确认设备出现 npm run android(或 Expo 下npx expo run:android)
iOS 真机(需 macOS)
- Xcode → Settings → Accounts 登录 Apple ID
- 用数据线连接 iPhone,Xcode 中选择该设备作为运行目标
- 首次运行需在设备上信任开发者证书:设置 → 通用 → VPN 与设备管理
鸿蒙真机/模拟器
- 从目标 RNOH 发行版的最小示例开始,确认其 RN、RNOH、DevEco 和 SDK 组合。
- 按该示例的步骤生成或打开
harmony/壳工程,连接华为真机或启动模拟器。 - 构建、安装、启动后,再按其文档配置 Metro 连通;不要把 Android/iOS 的命令或目录结构直接套用。
RNOH 的架构细节与依赖版本匹配见 05-harmonyos-rnoh-api。
目录结构速读(bare 工程)
MyFirstApp/
├── App.tsx / index.js # JS 入口与根组件
├── android/ # Android 原生工程(Gradle)
├── ios/ # iOS 原生工程(Xcode + CocoaPods)
├── harmony/ # 仅在所选 RNOH 集成方案创建;不是每个 RN 工程都自带
├── metro.config.js # Metro 打包配置
├── babel.config.js # 语法转换配置
└── package.json # 依赖与 npm scripts
日常 90% 的改动只发生在 App.tsx 与 src/ 业务代码里;android/、ios/、harmony/ 只在写原生模块或改构建配置时才打开。
最佳实践
第一个应用只需一个能解释清楚的界面:状态在哪里、点击如何改变状态、屏幕为何更新。逻辑增长后再拆页面与 Provider,不必先制造完整目录。用真机验证一次点击、输入和重启,确认连接的确是当前构建。
“修改不生效”先检查 Metro 进程、端口和设备连接,再考虑重置缓存。Expo prebuild 管理的原生文件可能被重新生成,修改应进入配置或插件;手工管理的原生工程则有不同维护方式,不应禁止所有 android/ios 目录修改。
常见问题
Q1: 红屏 “Unable to load bundle”?
A: 设备与 Metro 不在同一网络(真机场景),执行 adb reverse tcp:8081 tcp:8081 让 Android 真机通过 USB 访问电脑的 Metro;iOS 真机在 Xcode 的 scheme 中确认 host 配置。
Q2: 修改代码不生效?
A: 先确认 Metro 终端没有报错;再试 Fast Refresh 开关(模拟器摇一摇/Cmd+D 菜单);最后 --reset-cache 重启 Metro。
Q3: 鸿蒙真机运行白屏?
A: 按 RNOH 版本对齐清单核对 react-native-harmony 与 DevEco SDK API 版本,详见 常见错误排查。
练习与实践
练习一:一个平台的完整运行记录
任务要求: 1. 选择 Android 或 iOS 的一个模拟器或真机,运行本节 App。 2. 输入 Ada、提交、清空输入再提交,记录三个屏幕状态。 3. 暂停 Metro 或断开设备与开发机的连接,记录出现的现象;恢复后重新加载。 4. 若要增加第二平台,重新构建并重复上述步骤,不能只打开 Web 预览代替。
评估标准: 记录能够说明实际运行的平台、构建方式、输入与两个输出,以及 Metro 不可达时的表现。第二、三平台的记录是后续扩展,不是本课通过前提。
练习二:认识启动链路
任务要求: 1. 观察并记录 Metro 终端里 bundle 编译、加载的日志输出 2. 故意制造一个 JS 语法错误,观察红屏与 Metro 终端报错的对应关系
提示: 红屏信息与 Metro 终端日志指向同一处源码位置,养成两边对照看的习惯。