react-native

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

React Native Development

React Native 开发

Build native iOS and Android apps with React Native and Expo.
使用React Native和Expo构建原生iOS和Android应用。

Framework Options

框架选择

FrameworkUse When
Expo (Recommended)Most apps, faster development, managed workflow
React Native CLINeed custom native modules, brownfield apps
Expo with Dev ClientBest of both - Expo DX with native modules

框架适用场景
Expo(推荐)大多数应用场景,开发速度更快,托管式工作流
React Native CLI需要自定义原生模块、混合应用场景
Expo with Dev Client兼顾两者优势 - Expo开发体验+原生模块支持

Project Setup

项目搭建

Expo (Recommended)

Expo(推荐)

bash
npx create-expo-app@latest my-app
cd my-app
npx expo start
bash
npx create-expo-app@latest my-app
cd my-app
npx expo start

React Native CLI

React Native CLI

bash
npx @react-native-community/cli init MyApp
cd MyApp
npx react-native run-ios  # or run-android

bash
npx @react-native-community/cli init MyApp
cd MyApp
npx react-native run-ios  # or run-android

Core Components

核心组件

Basic Structure

基础结构

tsx
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",
  },
});
tsx
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",
  },
});

Common Components

常用组件

tsx
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"
/>

tsx
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"
/>

Navigation

导航

React Navigation Setup

React Navigation 配置

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

Stack Navigation

栈导航

tsx
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();
tsx
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();

Tab Navigation

标签导航

tsx
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>
  );
}

tsx
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>
  );
}

State Management

状态管理

Zustand (Recommended)

Zustand(推荐)

tsx
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),
    },
  ),
);
tsx
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

React Query

tsx
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}
    />
  );
}

tsx
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}
    />
  );
}

Styling

样式处理

StyleSheet

StyleSheet

tsx
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
  },
});
tsx
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 (Tailwind for RN)

NativeWind(RN版Tailwind)

bash
npm install nativewind tailwindcss
tsx
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>
  );
}

bash
npm install nativewind tailwindcss
tsx
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>
  );
}

Native Features

原生功能

Camera (Expo)

相机(Expo)

tsx
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>
  );
}
tsx
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>
  );
}

Push Notifications (Expo)

推送通知(Expo)

tsx
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();
}, []);
tsx
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();
}, []);

Location

定位

tsx
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,
  };
}

tsx
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,
  };
}

Performance

性能优化

FlatList Optimization

FlatList 优化

tsx
<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>
));
tsx
<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>
));

Image Optimization

图片优化

tsx
import { Image } from "expo-image";

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

tsx
import { Image } from "expo-image";

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

Testing

测试

Jest + React Native Testing Library

Jest + React Native Testing Library

tsx
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"), "test@example.com");
    fireEvent.changeText(getByPlaceholderText("Password"), "password123");
    fireEvent.press(getByText("Login"));

    await waitFor(() => {
      expect(onLogin).toHaveBeenCalledWith({
        email: "test@example.com",
        password: "password123",
      });
    });
  });
});

tsx
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"), "test@example.com");
    fireEvent.changeText(getByPlaceholderText("Password"), "password123");
    fireEvent.press(getByText("Login"));

    await waitFor(() => {
      expect(onLogin).toHaveBeenCalledWith({
        email: "test@example.com",
        password: "password123",
      });
    });
  });
});

App Store Deployment

App Store 部署

Expo EAS Build

Expo EAS Build

bash
undefined
bash
undefined

Install EAS CLI

Install EAS CLI

npm install -g eas-cli
npm install -g eas-cli

Configure

Configure

eas build:configure
eas build:configure

Build for iOS

Build for iOS

eas build --platform ios
eas build --platform ios

Build for Android

Build for Android

eas build --platform android
eas build --platform android

Submit to stores

Submit to stores

eas submit --platform ios eas submit --platform android
undefined
eas submit --platform ios eas submit --platform android
undefined

app.json Configuration

app.json 配置

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
    }
  }
}

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
    }
  }
}

Best Practices

最佳实践

DO:

建议:

  • Use Expo for faster development
  • Implement proper TypeScript types
  • Use FlashList for large lists
  • Handle keyboard properly
  • Test on real devices
  • 使用Expo提升开发速度
  • 实现规范的TypeScript类型
  • 针对大型列表使用FlashList
  • 妥善处理键盘交互
  • 在真实设备上测试

DON'T:

避免:

  • Inline styles in render
  • Skip memoization for lists
  • Ignore platform differences
  • Block JS thread with heavy computation
  • Forget to handle deep linking

  • 在渲染函数中内联样式
  • 忽略列表组件的 memoization
  • 忽视平台差异
  • 用重计算阻塞JS线程
  • 忘记处理深度链接

New Architecture (React Native 0.76+)

新架构(React Native 0.76+)

React Native's New Architecture replaces the legacy bridge with a more performant, synchronous layer.
React Native新架构用更高效的同步层替代了传统桥接层。

Core Components

核心组件

ComponentReplacesBenefit
FabricPaperSynchronous rendering, concurrent features
TurboModulesNative ModulesLazy loading, synchronous access, type safety
JSIBridgeDirect JS-to-native communication, no JSON
Bridgeless ModeBridgeRemoves bridge entirely, lower memory usage
组件替代对象优势
FabricPaper同步渲染,支持并发特性
TurboModulesNative Modules懒加载、同步访问、类型安全
JSIBridgeJS与原生直接通信,无需JSON序列化
Bridgeless ModeBridge完全移除桥接层,降低内存占用

Enabling New Architecture

启用新架构

json
// app.json (Expo)
{
  "expo": {
    "newArchEnabled": true
  }
}
ruby
undefined
json
// app.json (Expo)
{
  "expo": {
    "newArchEnabled": true
  }
}
ruby
undefined

ios/Podfile (bare React Native)

ios/Podfile (原生React Native)

ENV['RCT_NEW_ARCH_ENABLED'] = '1'
undefined
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
undefined

TurboModules (Type-Safe Native Modules)

TurboModules(类型安全原生模块)

typescript
// 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");
typescript
// 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 Components (New Renderer)

Fabric Components(新渲染器)

Fabric enables concurrent rendering features from React 18/19, including Suspense for data fetching, transitions, and automatic batching.

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

Expo Router (File-Based Routing)

Expo Router(基于文件的路由)

bash
npx create-expo-app@latest --template tabs
bash
npx create-expo-app@latest --template tabs

File Structure

文件结构

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          # 根布局
├── index.tsx            # 首页 (/)
├── (tabs)/              # 标签组
│   ├── _layout.tsx      # 标签导航器
│   ├── index.tsx        # 第一个标签页
│   └── settings.tsx     # 设置标签页
├── [id].tsx             # 动态路由 (/123)
├── modal.tsx            # 模态屏
└── +not-found.tsx       # 404页面

Layout and Navigation

布局与导航

tsx
// 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();

tsx
// 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>
  );
}

// 类型安全链接导航
import { Link, router } from "expo-router";

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

// 编程式导航
router.push("/settings");
router.replace("/login");
router.back();

React 19 Compatibility

React 19 兼容性

React Native supports React 19 features when using New Architecture:
  • use() hook for reading promises and context
  • ref as prop (no forwardRef needed)
  • React Compiler support for auto-memoization
  • useActionState and useOptimistic for form handling

使用新架构时,React Native支持React 19特性:
  • use() hook 用于读取Promise和上下文
  • ref作为属性(无需forwardRef)
  • React Compiler 支持自动memoization
  • useActionStateuseOptimistic 用于表单处理

EAS Update (OTA Updates)

EAS Update(OTA更新)

bash
undefined
bash
undefined

Install EAS CLI

Install EAS CLI

npm install -g eas-cli
npm install -g eas-cli

Configure updates

Configure updates

eas update:configure
eas update:configure

Push an OTA update (no app store review)

推送OTA更新(无需应用商店审核)

eas update --branch production --message "Bug fix for login" eas update --branch preview --message "New feature preview"

```typescript
// 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
  }
}

eas update --branch production --message "Bug fix for login" eas update --branch preview --message "New feature preview"

```typescript
// 在应用中检查更新
import * as Updates from "expo-updates";

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

Hermes Engine

Hermes 引擎

Hermes is the default JS engine for React Native, optimized for mobile:
  • Faster startup via bytecode precompilation
  • Lower memory usage than JavaScriptCore
  • Better debugging with Chrome DevTools Protocol
json
// app.json - Hermes is enabled by default in Expo SDK 52+
{
  "expo": {
    "jsEngine": "hermes"
  }
}

Hermes是React Native的默认JS引擎,专为移动设备优化:
  • 更快启动 通过字节码预编译
  • 更低内存 占用比JavaScriptCore小
  • 更好调试 支持Chrome DevTools协议
json
// app.json - Expo SDK 52+默认启用Hermes
{
  "expo": {
    "jsEngine": "hermes"
  }
}

Expo SDK 52+ Features

Expo SDK 52+ 特性

FeatureDescription
Expo Router v4File-based routing with API routes
expo-camera (next)Camera API with barcode scanning
expo-videoModern video player replacing expo-av
expo-sqliteSQLite with synchronous API via JSI
DOM ComponentsRender web components inside React Native
React 19 supportFull React 19 features with New Architecture
Universal linksDeep linking configured via app.json
特性描述
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配置深度链接