react-native
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Native Development
React Native 开发
Build native iOS and Android apps with React Native and Expo.
使用React Native和Expo构建原生iOS和Android应用。
Framework Options
框架选择
| Framework | Use When |
|---|---|
| Expo (Recommended) | Most apps, faster development, managed workflow |
| React Native CLI | Need custom native modules, brownfield apps |
| Expo with Dev Client | Best 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 startbash
npx create-expo-app@latest my-app
cd my-app
npx expo startReact Native CLI
React Native CLI
bash
npx @react-native-community/cli init MyApp
cd MyApp
npx react-native run-ios # or run-androidbash
npx @react-native-community/cli init MyApp
cd MyApp
npx react-native run-ios # or run-androidCore 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-contextbash
npm install @react-navigation/native @react-navigation/native-stack
npx expo install react-native-screens react-native-safe-area-contextStack 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 tailwindcsstsx
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 tailwindcsstsx
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
undefinedbash
undefinedInstall 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
undefinedeas submit --platform ios
eas submit --platform android
undefinedapp.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
核心组件
| Component | Replaces | Benefit |
|---|---|---|
| Fabric | Paper | Synchronous rendering, concurrent features |
| TurboModules | Native Modules | Lazy loading, synchronous access, type safety |
| JSI | Bridge | Direct JS-to-native communication, no JSON |
| Bridgeless Mode | Bridge | Removes bridge entirely, lower memory usage |
| 组件 | 替代对象 | 优势 |
|---|---|---|
| Fabric | Paper | 同步渲染,支持并发特性 |
| TurboModules | Native Modules | 懒加载、同步访问、类型安全 |
| JSI | Bridge | JS与原生直接通信,无需JSON序列化 |
| Bridgeless Mode | Bridge | 完全移除桥接层,降低内存占用 |
Enabling New Architecture
启用新架构
json
// app.json (Expo)
{
"expo": {
"newArchEnabled": true
}
}ruby
undefinedjson
// app.json (Expo)
{
"expo": {
"newArchEnabled": true
}
}ruby
undefinedios/Podfile (bare React Native)
ios/Podfile (原生React Native)
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
undefinedENV['RCT_NEW_ARCH_ENABLED'] = '1'
undefinedTurboModules (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 tabsbash
npx create-expo-app@latest --template tabsFile 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 screenapp/
├── _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
- useActionState 和 useOptimistic 用于表单处理
EAS Update (OTA Updates)
EAS Update(OTA更新)
bash
undefinedbash
undefinedInstall 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+ 特性
| Feature | Description |
|---|---|
| Expo Router v4 | File-based routing with API routes |
| expo-camera (next) | Camera API with barcode scanning |
| expo-video | Modern video player replacing expo-av |
| expo-sqlite | SQLite with synchronous API via JSI |
| DOM Components | Render web components inside React Native |
| React 19 support | Full React 19 features with New Architecture |
| Universal links | Deep 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配置深度链接 |