mcpField
R
react-native

react-native

github.com/travisjneuman开发107153 次安装1 天前更新
代码仓库

关于 react-native

面向 React Native 应用开发者的实践指南,涵盖 Expo 与 React Native CLI、导航、样式和原生能力。还介绍新架构、Expo Router、EAS 更新及移动端性能与发布流程。

主要功能

  1. 01比较 Expo 与 React Native CLI
  2. 02配置导航、路由与样式
  3. 03使用相机、通知和定位
  4. 04介绍 Fabric、TurboModules 与 JSI
  5. 05配置 EAS 构建和 OTA 更新
  6. 06优化列表与 Hermes 运行时

适用场景

  1. 01搭建 iOS 和 Android 应用项目
  2. 02为应用添加导航与原生能力
  3. 03配置应用构建及空中更新
  4. 04优化移动应用的列表性能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/travisjneuman/.claude --skill react-native

安装后在 Claude Code 中输入/react-native确认已加载。

详细介绍

React Native 开发

使用 React Native 和 Expo 构建原生 iOS 和 Android 应用。

框架选项

框架适用场景
Expo(推荐)大多数应用、更快的开发速度、托管工作流
React Native CLI需要自定义原生模块、棕地应用
Expo with Dev Client兼得两者优势:Expo 开发体验与原生模块

项目设置

Expo(推荐)
npx create-expo-app@latest my-app
cd my-app
npx expo start
React Native CLI
npx @react-native-community/cli init MyApp
cd MyApp
npx react-native run-ios  # or run-android

核心组件

基本结构
import { View, Text, StyleSheet, ScrollView, SafeAreaView } from "react-native";

export default function App() {
  return (
    <SafeAreaView style={styles.container}>
      <ScrollView contentContainerStyle={styles.content}>
        <Text style={styles.title}>Hello World</Text>
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
  },
  content: {
    padding: 16,
  },
  title: {
    fontSize: 24,
    fontWeight: "bold",
  },
});
常用组件
import {
  View,
  Text,
  Image,
  TextInput,
  TouchableOpacity,
  Pressable,
  FlatList,
  ActivityIndicator,
  Modal,
  Switch,
} from 'react-native';

// Touchable with feedback
<Pressable
  onPress={handlePress}
  style={({ pressed }) => [
    styles.button,
    pressed && styles.buttonPressed,
  ]}
>
  <Text>Press Me</Text>
</Pressable>

// Text Input
<TextInput
  value={text}
  onChangeText={setText}
  placeholder="Enter text"
  style={styles.input}
  autoCapitalize="none"
  keyboardType="email-address"
  returnKeyType="done"
  onSubmitEditing={handleSubmit}
/>

// Image
<Image
  source={{ uri: 'https://example.com/image.jpg' }}
  style={{ width: 100, height: 100 }}
  resizeMode="cover"
/>

导航

React Navigation 设置
npm install @react-navigation/native @react-navigation/native-stack
npx expo install react-native-screens react-native-safe-area-context
堆栈导航
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";

type RootStackParamList = {
  Home: undefined;
  Details: { itemId: string };
};

const Stack = createNativeStackNavigator<RootStackParamList>();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={{ title: "My App" }}
        />
        <Stack.Screen
          name="Details"
          component={DetailsScreen}
          options={({ route }) => ({ title: route.params.itemId })}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

// Navigate
navigation.navigate("Details", { itemId: "123" });

// Go back
navigation.goBack();
标签导航
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Ionicons } from "@expo/vector-icons";

const Tab = createBottomTabNavigator();

function TabNavigator() {
  return (
    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: ({ focused, color, size }) => {
          const iconName =
            route.name === "Home"
              ? focused
                ? "home"
                : "home-outline"
              : focused
                ? "settings"
                : "settings-outline";
          return <Ionicons name={iconName} size={size} color={color} />;
        },
      })}
    >
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );
}

状态管理

Zustand(推荐)
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
import AsyncStorage from "@react-native-async-storage/async-storage";

interface AuthStore {
  user: User | null;
  token: string | null;
  login: (user: User, token: string) => void;
  logout: () => void;
}

export const useAuthStore = create<AuthStore>()(
  persist(
    (set) => ({
      user: null,
      token: null,
      login: (user, token) => set({ user, token }),
      logout: () => set({ user: null, token: null }),
    }),
    {
      name: "auth-storage",
      storage: createJSONStorage(() => AsyncStorage),
    },
  ),
);
React Query
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
} from "@tanstack/react-query";

const queryClient = new QueryClient();

// Wrap app
<QueryClientProvider client={queryClient}>
  <App />
</QueryClientProvider>;

// Use in component
function ItemList() {
  const { data, isLoading, error, refetch } = useQuery({
    queryKey: ["items"],
    queryFn: fetchItems,
  });

  if (isLoading) return <ActivityIndicator />;
  if (error) return <Text>Error: {error.message}</Text>;

  return (
    <FlatList
      data={data}
      renderItem={({ item }) => <ItemRow item={item} />}
      keyExtractor={(item) => item.id}
      onRefresh={refetch}
      refreshing={isLoading}
    />
  );
}

样式

StyleSheet
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#ffffff",
  },
  row: {
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    padding: 16,
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderBottomColor: "#ccc",
  },
  text: {
    fontSize: 16,
    color: "#333",
  },
  shadow: {
    shadowColor: "#000",
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3, // Android
  },
});
NativeWind(适用于 RN 的 Tailwind)
npm install nativewind tailwindcss
import { Text, View } from "react-native";

export function Card() {
  return (
    <View className="bg-white rounded-lg p-4 shadow-md">
      <Text className="text-lg font-bold text-gray-900">Card Title</Text>
      <Text className="text-gray-600 mt-2">Card content goes here</Text>
    </View>
  );
}

原生功能

相机(Expo)
import { Camera, CameraType } from "expo-camera";

function CameraScreen() {
  const [permission, requestPermission] = Camera.useCameraPermissions();
  const cameraRef = useRef<Camera>(null);

  const takePicture = async () => {
    if (cameraRef.current) {
      const photo = await cameraRef.current.takePictureAsync();
      console.log(photo.uri);
    }
  };

  if (!permission?.granted) {
    return <Button title="Grant Permission" onPress={requestPermission} />;
  }

  return (
    <Camera ref={cameraRef} style={styles.camera} type={CameraType.back}>
      <TouchableOpacity onPress={takePicture}>
        <Text>Take Photo</Text>
      </TouchableOpacity>
    </Camera>
  );
}
推送通知(Expo)
import * as Notifications from "expo-notifications";
import { useEffect } from "react";

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: false,
  }),
});

async function registerForPushNotifications() {
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== "granted") return;

  const token = await Notifications.getExpoPushTokenAsync();
  return token.data;
}

// Listen for notifications
useEffect(() => {
  const subscription = Notifications.addNotificationReceivedListener(
    (notification) => {
      console.log(notification);
    },
  );
  return () => subscription.remove();
}, []);
位置
import * as Location from "expo-location";

async function getLocation() {
  const { status } = await Location.requestForegroundPermissionsAsync();
  if (status !== "granted") return;

  const location = await Location.getCurrentPositionAsync({});
  return {
    latitude: location.coords.latitude,
    longitude: location.coords.longitude,
  };
}

性能

FlatList 优化
<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={(item) => item.id}
  // Performance optimizations
  initialNumToRender={10}
  maxToRenderPerBatch={10}
  windowSize={5}
  removeClippedSubviews={true}
  getItemLayout={(data, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>;

// Memoize render item
const renderItem = useCallback(
  ({ item }: { item: Item }) => <MemoizedItem item={item} />,
  [],
);

const MemoizedItem = memo(({ item }: { item: Item }) => (
  <View>
    <Text>{item.name}</Text>
  </View>
));
图像优化
import { Image } from "expo-image";

<Image
  source={{ uri: imageUrl }}
  style={{ width: 200, height: 200 }}
  contentFit="cover"
  placeholder={blurhash}
  transition={200}
  cachePolicy="memory-disk"
/>;

测试

Jest + React Native Testing Library
import { render, fireEvent, waitFor } from "@testing-library/react-native";
import { LoginScreen } from "./LoginScreen";

describe("LoginScreen", () => {
  it("should login successfully", async () => {
    const onLogin = jest.fn();
    const { getByPlaceholderText, getByText } = render(
      <LoginScreen onLogin={onLogin} />,
    );

    fireEvent.changeText(getByPlaceholderText("Email"), "[email protected]");
    fireEvent.changeText(getByPlaceholderText("Password"), "password123");
    fireEvent.press(getByText("Login"));

    await waitFor(() => {
      expect(onLogin).toHaveBeenCalledWith({
        email: "[email protected]",
        password: "password123",
      });
    });
  });
});

App Store 部署

Expo EAS Build
# Install EAS CLI
npm install -g eas-cli

# Configure
eas build:configure

# Build for iOS
eas build --platform ios

# Build for Android
eas build --platform android

# Submit to stores
eas submit --platform ios
eas submit --platform android
app.json 配置
{
  "expo": {
    "name": "My App",
    "slug": "my-app",
    "version": "1.0.0",
    "ios": {
      "bundleIdentifier": "com.mycompany.myapp",
      "buildNumber": "1"
    },
    "android": {
      "package": "com.mycompany.myapp",
      "versionCode": 1
    }
  }
}

最佳实践

应该:
  • 使用 Expo 加快开发
  • 实现适当的 TypeScript 类型
  • 对大型列表使用 FlashList
  • 妥善处理键盘
  • 在真实设备上测试
不应该:
  • 在渲染时使用内联样式
  • 对列表跳过记忆化
  • 忽略平台差异
  • 使用繁重计算阻塞 JS 线程
  • 忘记处理深层链接

新架构(React Native 0.76+)

React Native 的新架构以性能更高的同步层取代了旧版桥接层。

核心组件
组件替代对象优势
FabricPaper同步渲染、并发功能
TurboModulesNative Modules延迟加载、同步访问、类型安全
JSIBridgeJS 与原生之间直接通信,无需 JSON
Bridgeless ModeBridge完全移除桥接层,降低内存用量
启用新架构
// app.json (Expo)
{
  "expo": {
    "newArchEnabled": true
  }
}
# ios/Podfile (bare React Native)
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
TurboModules(类型安全的原生模块)
// NativeCalculator.ts
import type { TurboModule } from "react-native";
import { TurboModuleRegistry } from "react-native";

export interface Spec extends TurboModule {
  add(a: number, b: number): number; // Synchronous!
  fetchData(url: string): Promise<string>; // Async
}

export default TurboModuleRegistry.getEnforcing<Spec>("NativeCalculator");
Fabric 组件(新渲染器)

Fabric 支持 React 18/19 的并发渲染功能,包括用于数据获取的 Suspense、过渡效果和自动批处理。


Expo Router(基于文件的路由)

npx create-expo-app@latest --template tabs
文件结构
app/
├── _layout.tsx          # Root layout
├── index.tsx            # Home screen (/)
├── (tabs)/              # Tab group
│   ├── _layout.tsx      # Tab navigator
│   ├── index.tsx        # First tab
│   └── settings.tsx     # Settings tab
├── [id].tsx             # Dynamic route (/123)
├── modal.tsx            # Modal screen
└── +not-found.tsx       # 404 screen
布局和导航
// app/_layout.tsx
import { Stack } from "expo-router";

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen name="modal" options={{ presentation: "modal" }} />
    </Stack>
  );
}

// Navigate with type-safe links
import { Link, router } from "expo-router";

<Link href="/settings">Settings</Link>
<Link href={{ pathname: "/[id]", params: { id: "123" } }}>Details</Link>

// Programmatic navigation
router.push("/settings");
router.replace("/login");
router.back();

React 19 兼容性

使用新架构时,React Native 支持 React 19 的功能:

  • 用于读取 Promise 和上下文的 use() hook
  • ref 作为 prop(无需 forwardRef)
  • 支持通过 React Compiler 自动记忆化
  • 用于表单处理的 useActionState 和 useOptimistic

EAS Update(OTA 更新)

# Install EAS CLI
npm install -g eas-cli

# Configure updates
eas update:configure

# Push an OTA update (no app store review)
eas update --branch production --message "Bug fix for login"
eas update --branch preview --message "New feature preview"
// Check for updates in app
import * as Updates from "expo-updates";

async function checkForUpdates() {
  const update = await Updates.checkForUpdateAsync();
  if (update.isAvailable) {
    await Updates.fetchUpdateAsync();
    await Updates.reloadAsync(); // Apply update
  }
}

Hermes 引擎

Hermes 是 React Native 的默认 JS 引擎,针对移动设备进行了优化:

  • 通过字节码预编译实现更快启动
  • 相比 JavaScriptCore,内存用量更低
  • 通过 Chrome DevTools Protocol 实现更好的调试体验
// app.json - Hermes is enabled by default in Expo SDK 52+
{
  "expo": {
    "jsEngine": "hermes"
  }
}

Expo SDK 52+ 功能

功能描述
Expo Router v4基于文件的路由,支持 API 路由
expo-camera (next)支持条形码扫描的相机 API
expo-video取代 expo-av 的现代视频播放器
expo-sqlite通过 JSI 提供同步 API 的 SQLite
DOM Components在 React Native 中渲染 Web 组件
React 19 support结合新架构支持 React 19 的全部功能
Universal links通过 app.json 配置深度链接

评论

支持 Markdown