Appearance
栈导航(Stack Navigator)
第五部分:路由导航(页面跳转)
栈导航(Stack Navigator)是 React Navigation 中最基本的导航器类型,用于实现页面之间的层级跳转,类似于 Web 应用中的浏览器历史记录。本文将详细介绍 Stack Navigator 的使用方法。
1. 基本使用
创建栈导航器
首先,需要创建一个栈导航器并定义屏幕:
jsx
import 'react-native-gesture-handler';
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
import DetailsScreen from './screens/DetailsScreen';
import ProfileScreen from './screens/ProfileScreen';
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}导航方法
在屏幕组件中,可以使用 navigation prop 提供的方法进行导航:
1. 跳转到新屏幕
jsx
// HomeScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Text style={styles.title}>首页</Text>
<Button
title="跳转到详情页"
onPress={() => navigation.navigate('Details')}
/>
<Button
title="跳转到个人资料页"
onPress={() => navigation.navigate('Profile')}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 20,
},
title: {
fontSize: 24,
marginBottom: 20,
},
});2. 返回上一页
jsx
// DetailsScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function DetailsScreen({ navigation }) {
return (
<View style={styles.container}>
<Text style={styles.title}>详情页</Text>
<Button
title="返回首页"
onPress={() => navigation.goBack()}
/>
<Button
title="返回上一页"
onPress={() => navigation.goBack()}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 20,
},
title: {
fontSize: 24,
marginBottom: 20,
},
});3. 重置导航栈
jsx
// ProfileScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function ProfileScreen({ navigation }) {
const handleReset = () => {
navigation.reset({
index: 0,
routes: [{ name: 'Home' }],
});
};
return (
<View style={styles.container}>
<Text style={styles.title}>个人资料页</Text>
<Button
title="返回首页"
onPress={() => navigation.goBack()}
/>
<Button
title="重置导航栈"
onPress={handleReset}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 20,
},
title: {
fontSize: 24,
marginBottom: 20,
},
});2. 导航选项配置
基本选项
可以为每个屏幕配置导航选项:
jsx
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: '首页',
headerStyle: {
backgroundColor: '#4CAF50',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
/>
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{
title: '详情页',
headerBackTitle: '返回',
}}
/>
</Stack.Navigator>全局选项
可以为整个导航器设置全局选项:
jsx
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#4CAF50',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerBackTitle: '返回',
}}
>
<Stack.Screen name="Home" component={HomeScreen} options={{ title: '首页' }} />
<Stack.Screen name="Details" component={DetailsScreen} options={{ title: '详情页' }} />
</Stack.Navigator>动态选项
可以根据路由参数动态设置导航选项:
jsx
<Stack.Screen
name="Details"
component={DetailsScreen}
options={({ route }) => ({
title: route.params?.title || '详情页',
})}
/>3. 参数传递
传递参数
在导航时可以传递参数:
jsx
// HomeScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Text style={styles.title}>首页</Text>
<Button
title="跳转到详情页(带参数)"
onPress={() => navigation.navigate('Details', {
id: 1,
title: '产品详情',
price: 99.99,
})}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 24,
marginBottom: 20,
},
});接收参数
在目标屏幕中接收参数:
jsx
// DetailsScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function DetailsScreen({ navigation, route }) {
const { id, title, price } = route.params || {};
return (
<View style={styles.container}>
<Text style={styles.title}>{title || '详情页'}</Text>
<Text style={styles.info}>ID: {id}</Text>
<Text style={styles.info}>价格: ¥{price}</Text>
<Button
title="返回首页"
onPress={() => navigation.goBack()}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 20,
},
title: {
fontSize: 24,
marginBottom: 20,
},
info: {
fontSize: 16,
},
});更新参数
可以使用 setParams 方法更新当前屏幕的参数:
jsx
// DetailsScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
export default function DetailsScreen({ navigation, route }) {
const { id, title, price } = route.params || {};
const updateTitle = () => {
navigation.setParams({ title: '更新后的标题' });
};
return (
<View style={styles.container}>
<Text style={styles.title}>{title || '详情页'}</Text>
<Text style={styles.info}>ID: {id}</Text>
<Text style={styles.info}>价格: ¥{price}</Text>
<Button
title="更新标题"
onPress={updateTitle}
/>
<Button
title="返回首页"
onPress={() => navigation.goBack()}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 20,
},
title: {
fontSize: 24,
marginBottom: 20,
},
info: {
fontSize: 16,
},
});4. 高级用法
自定义导航栏
可以使用 header 选项自定义导航栏:
jsx
import React from 'react';
import { View, Text, Button, StyleSheet, TouchableOpacity } from 'react-native';
import { createStackNavigator } from '@react-navigation/stack';
const Stack = createStackNavigator();
function CustomHeader({ navigation, route }) {
return (
<View style={styles.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={styles.backButton}>
<Text style={styles.backText}>←</Text>
</TouchableOpacity>
<Text style={styles.headerTitle}>{route.params?.title || '自定义标题'}</Text>
<TouchableOpacity style={styles.rightButton}>
<Text style={styles.rightText}>菜单</Text>
</TouchableOpacity>
</View>
);
}
export default function CustomHeaderExample() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: '首页' }}
/>
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{
header: (props) => <CustomHeader {...props} />,
}}
/>
</Stack.Navigator>
);
}
const styles = StyleSheet.create({
header: {
height: 60,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: '#4CAF50',
paddingHorizontal: 16,
},
headerTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#fff',
},
backButton: {
padding: 8,
},
backText: {
fontSize: 24,
color: '#fff',
},
rightButton: {
padding: 8,
},
rightText: {
fontSize: 16,
color: '#fff',
},
});隐藏导航栏
可以使用 headerShown 选项隐藏导航栏:
jsx
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ headerShown: false }}
/>透明导航栏
可以创建透明导航栏:
jsx
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerTransparent: true,
headerTintColor: '#fff',
headerTitle: '',
}}
/>动画配置
可以配置导航动画:
jsx
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#4CAF50',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
cardStyleInterpolator: ({ current, layouts }) => {
return {
cardStyle: {
transform: [
{
translateX: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.width, 0],
}),
},
],
},
};
},
}}
>
{/* 屏幕配置 */}
</Stack.Navigator>5. 常见问题与解决方案
问题 1:导航栏标题不显示
问题:导航栏标题不显示或显示不正确。
解决方案:
- 确保为屏幕设置了
title选项 - 检查是否使用了
headerTitle选项覆盖了默认标题 - 对于动态标题,确保正确使用了
route.params
问题 2:参数传递失败
问题:无法在屏幕之间传递参数。
解决方案:
- 确保使用正确的导航方法:
navigation.navigate('ScreenName', { params }) - 确保在目标屏幕中正确获取参数:
const { route } = props; const { params } = route; - 检查参数是否为可序列化的值(避免传递函数或循环引用)
问题 3:导航栈管理
问题:导航栈管理混乱,出现重复屏幕或无法正确返回。
解决方案:
- 使用
navigation.reset()重置导航栈 - 使用
navigation.replace()替换当前屏幕 - 使用
navigation.navigate()时,如果目标屏幕已在栈中,会返回该屏幕而不是创建新实例
问题 4:自定义导航栏样式
问题:无法自定义导航栏样式。
解决方案:
- 使用
headerStyle、headerTintColor、headerTitleStyle等选项 - 对于复杂的自定义,使用
header选项提供自定义组件 - 注意平台差异,某些样式在 iOS 和 Android 上可能表现不同
6. 最佳实践
1. 组织导航结构
将导航相关代码组织到单独的文件中:
jsx
// navigation/StackNavigator.js
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from '../screens/HomeScreen';
import DetailsScreen from '../screens/DetailsScreen';
import ProfileScreen from '../screens/ProfileScreen';
const Stack = createStackNavigator();
export default function AppStackNavigator() {
return (
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#4CAF50',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<Stack.Screen name="Home" component={HomeScreen} options={{ title: '首页' }} />
<Stack.Screen name="Details" component={DetailsScreen} options={{ title: '详情页' }} />
<Stack.Screen name="Profile" component={ProfileScreen} options={{ title: '个人资料' }} />
</Stack.Navigator>
);
}2. 使用类型定义
为导航参数添加类型定义:
typescript
// navigation/types.ts
export type RootStackParamList = {
Home: undefined;
Details: {
id: number;
title: string;
price?: number;
};
Profile: undefined;
};
// 使用类型
import { NativeStackScreenProps } from '@react-navigation/native-stack';
export default function HomeScreen({
navigation,
}: NativeStackScreenProps<RootStackParamList, 'Home'>) {
return (
<View>
<Button
title="跳转到详情页"
onPress={() => navigation.navigate('Details', { id: 1, title: '产品详情' })}
/>
</View>
);
}3. 合理使用导航方法
navigation.navigate():跳转到新屏幕,会添加到导航栈navigation.goBack():返回上一页navigation.popToTop():返回导航栈的第一个屏幕navigation.replace():替换当前屏幕,不会添加到导航栈navigation.reset():重置整个导航栈
4. 处理深链接
配置深链接以支持从外部打开应用的特定页面:
jsx
// App.js
import React from 'react';
import { NavigationContainer, useLinking } from '@react-navigation/native';
import AppStackNavigator from './navigation/StackNavigator';
function App() {
const { getInitialState } = useLinking({
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: '',
Details: 'details/:id',
Profile: 'profile',
},
},
});
const [isReady, setIsReady] = React.useState(false);
const [initialState, setInitialState] = React.useState(null);
React.useEffect(() => {
const prepare = async () => {
try {
const state = await getInitialState();
setInitialState(state);
} catch (e) {
console.error(e);
} finally {
setIsReady(true);
}
};
prepare();
}, [getInitialState]);
if (!isReady) {
return null;
}
return (
<NavigationContainer initialState={initialState}>
<AppStackNavigator />
</NavigationContainer>
);
}
export default App;7. 总结
栈导航(Stack Navigator)是 React Navigation 中最常用的导航器类型,用于实现页面之间的层级跳转。通过本文的学习,你应该掌握了以下内容:
- Stack Navigator 的基本使用方法
- 导航方法的使用(navigate、goBack、reset 等)
- 导航选项的配置
- 参数传递和接收
- 高级用法(自定义导航栏、隐藏导航栏、动画配置)
- 常见问题的解决方案
- 最佳实践
在实际开发中,合理使用 Stack Navigator,可以创建出流畅、直观的页面导航体验,使应用更加专业和易用。
