React Native 跨平台开发实战:从环境搭建到双端发布

By | 2026年9月19日

为什么选择 React Native?

在移动端开发领域,跨平台方案一直备受关注。React Native(RN)凭借其「Learn once, write anywhere」的理念,允许开发者使用 JavaScript 和 React 构建真正的原生应用,同时保持接近原生的性能体验。

本文将以一个待办事项(TodoList)应用为例,带你从零开始完成一个可运行在 iOS 和 Android 上的跨平台应用,并分享实际开发中的最佳实践与常见陷阱。

环境搭建:避开新手坑

1. 安装 Node 与 Watchman

首先确保 Node.js 版本 >= 16。推荐使用 nvm 管理 Node 版本:


nvm install 18
nvm use 18

macOS 用户建议安装 Watchman 以提升文件监听性能:


brew install watchman

2. 安装 React Native CLI

官方推荐使用 npx react-native init 或 Expo。本文选择纯 RN CLI,以便更灵活地集成原生模块。


npx react-native@latest init TodoApp --version 0.72.6
cd TodoApp

> 💡 注意:RN 版本迭代快,建议锁定一个稳定版本(如 0.72.x),避免因版本不兼容导致构建失败。

3. iOS 环境配置

  • 安装 Xcode(最新版)并同意许可协议:sudo xcodebuild -license accept
  • 安装 CocoaPods:sudo gem install cocoapods
  • 进入 ios 目录安装依赖:cd ios && pod install

4. Android 环境配置

  • 安装 JDK 17(RN 0.72 要求)
  • 下载 Android Studio,安装 SDK Platform 33 和 Build-Tools 33.0.0
  • 配置环境变量:

export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools

> ⚠️ 常见坑:Windows 用户需确保 Hyper-V 或 Intel HAXM 已开启,否则模拟器无法启动。

项目结构设计

一个清晰的目录结构能极大提升可维护性。我们采用如下组织方式:


src/
  components/    # 可复用 UI 组件
  screens/       # 页面组件
  navigation/    # 导航配置
  store/         # 状态管理
  utils/         # 工具函数
  services/      # API 服务
App.tsx

核心功能实现

1. 状态管理:使用 Zustand 替代 Redux

对于中小型应用,Zustand 更轻量且易于上手。安装:


npm install zustand

创建 store:


// src/store/todoStore.ts
import { create } from 'zustand';

interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

interface TodoState {
  todos: Todo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: string) => void;
  removeTodo: (id: string) => void;
}

export const useTodoStore = create<TodoState>((set) => ({
  todos: [],
  addTodo: (text) =>
    set((state) => ({
      todos: [
        ...state.todos,
        { id: Date.now().toString(), text, completed: false },
      ],
    })),
  toggleTodo: (id) =>
    set((state) => ({
      todos: state.todos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      ),
    })),
  removeTodo: (id) =>
    set((state) => ({
      todos: state.todos.filter((todo) => todo.id !== id),
    })),
}));

2. 页面组件:TodoList 与 AddTodo

安装必要的 UI 库(可选,本文用原生组件):


npm install react-native-vector-icons
npx react-native link react-native-vector-icons

编写主页面:


// src/screens/HomeScreen.tsx
import React, { useState } from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  FlatList,
  StyleSheet,
  KeyboardAvoidingView,
  Platform,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { useTodoStore } from '../store/todoStore';

const HomeScreen = () => {
  const [input, setInput] = useState('');
  const { todos, addTodo, toggleTodo, removeTodo } = useTodoStore();

  const handleAdd = () => {
    if (input.trim()) {
      addTodo(input.trim());
      setInput('');
    }
  };

  return (
    <KeyboardAvoidingView
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      style={styles.container}
    >
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.input}
          placeholder="添加新任务..."
          value={input}
          onChangeText={setInput}
          onSubmitEditing={handleAdd}
        />
        <TouchableOpacity style={styles.addButton} onPress={handleAdd}>
          <Icon name="add" size={24} color="#fff" />
        </TouchableOpacity>
      </View>
      <FlatList
        data={todos}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View style={styles.todoItem}>
            <TouchableOpacity onPress={() => toggleTodo(item.id)}>
              <Icon
                name={item.completed ? 'checkbox' : 'square-outline'}
                size={24}
                color={item.completed ? '#4caf50' : '#ccc'}
              />
            </TouchableOpacity>
            <Text
              style={[
                styles.todoText,
                item.completed && styles.completedText,
              ]}
            >
              {item.text}
            </Text>
            <TouchableOpacity onPress={() => removeTodo(item.id)}>
              <Icon name="trash" size={24} color="#f44336" />
            </TouchableOpacity>
          </View>
        )}
      />
    </KeyboardAvoidingView>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, padding: 16, backgroundColor: '#f5f5f5' },
  inputContainer: { flexDirection: 'row', marginBottom: 16 },
  input: {
    flex: 1,
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    paddingHorizontal: 12,
    height: 48,
    backgroundColor: '#fff',
  },
  addButton: {
    width: 48,
    height: 48,
    backgroundColor: '#2196f3',
    borderRadius: 8,
    justifyContent: 'center',
    alignItems: 'center',
    marginLeft: 8,
  },
  todoItem: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#fff',
    padding: 16,
    borderRadius: 8,
    marginBottom: 8,
  },
  todoText: { flex: 1, marginHorizontal: 12, fontSize: 16 },
  completedText: { textDecorationLine: 'line-through', color: '#aaa' },
});

export default HomeScreen;

3. 导航:React Navigation v6

安装依赖:


npm install @react-navigation/native @react-navigation/native-stack
npm install react-native-screens react-native-safe-area-context

配置导航:


// App.tsx
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from './src/screens/HomeScreen';

const Stack = createNativeStackNavigator();

const App = () => (
  <NavigationContainer>
    <Stack.Navigator>
      <Stack.Screen name="Home" component={HomeScreen} options={{ title: '待办事项' }} />
    </Stack.Navigator>
  </NavigationContainer>
);

export default App;

性能优化最佳实践

1. 使用 FlatList 的优化属性

对于长列表,务必添加 keyExtractor、getItemLayout 和 removeClippedSubviews:


<FlatList
  data={todos}
  keyExtractor={(item) => item.id}
  getItemLayout={(_, index) => ({ length: 72, offset: 72 * index, index })}
  removeClippedSubviews={true}
  initialNumToRender={10}
  maxToRenderPerBatch={10}
  windowSize={5}
/>

2. 避免内联函数与对象

在 render 中创建新函数或对象会导致子组件不必要的重渲染。使用 useCallback 和 useMemo:


const renderItem = useCallback(({ item }) => (
  <TodoItem item={item} onToggle={toggleTodo} onRemove={removeTodo} />
), [toggleTodo, removeTodo]);

3. 图片优化

使用 react-native-fast-image 替代默认 Image 组件,支持缓存和优先级。

原生模块调用:获取设备信息

有时需要访问原生 API。以获取电池电量为例,安装社区模块:


npm install react-native-device-info
cd ios && pod install

使用:


import DeviceInfo from 'react-native-device-info';

const batteryLevel = await DeviceInfo.getBatteryLevel();
console.log(`当前电量:${(batteryLevel * 100).toFixed(0)}%`);

> 💡 提示:RN 0.72 支持 TurboModules,但社区模块可能尚未适配。若遇到编译错误,可暂时降级使用旧架构。

双端打包与发布

Android 打包

  1. 生成签名密钥:

keytool -genkeypair -v -storetype PKCS12 -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
  1. 配置 android/gradle.properties:

MYAPP_UPLOAD_STORE_FILE=my-release-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****
  1. 在 android/app/build.gradle 中配置签名并执行:

cd android && ./gradlew assembleRelease

生成的 APK 位于 android/app/build/outputs/apk/release/。

iOS 打包

  1. 在 Xcode 中配置签名与证书。
  2. 选择 Generic iOS Device,执行 Product > Archive。
  3. 通过 Organizer 上传到 App Store Connect。

> ⚠️ 注意:iOS 打包必须使用 macOS,且需要付费开发者账号($99/年)。

常见踩坑与解决方案

  1. Metro 缓存问题:修改代码后不生效,尝试 npx react-native start --reset-cache。
  2. Android 9+ 网络请求失败:需在 AndroidManifest.xml 中添加 android:usesCleartextTraffic="true" 或使用 HTTPS。
  3. 键盘遮挡输入框:使用 KeyboardAvoidingView 并设置合适的 behavior。
  4. iOS 安全区域:使用 SafeAreaView 包裹页面内容。
  5. Hermes 引擎:RN 0.70+ 默认启用 Hermes,可显著提升启动速度,但需注意部分库兼容性。

总结与下一步

通过本文,你已完成一个具备增删改查、状态管理和导航的跨平台应用,并掌握了性能优化与发布流程。React Native 生态仍在快速演进,建议进一步探索:

  • 新架构(Fabric & TurboModules):了解如何迁移以获得更好性能。
  • Expo 生态:简化开发与部署流程。
  • React Native Web:复用代码到 Web 端。
  • 测试:使用 Jest 和 Detox 编写单元与端到端测试。

跨平台开发不是银弹,但 React Native 在开发效率与用户体验之间取得了良好平衡。希望本文能帮助你少走弯路,快速交付高质量应用。