React Native 的路由栈管理、页面销毁、返回拦截 ,如果现在是 React Native 新项目,基本都是围绕 React Navigation 7.x(6.x 思路也一样) 来讲。
下面按面试和实际开发两个角度总结。
一、RN 路由栈(Navigation Stack)
React Navigation 的 Stack Navigator,本质就是一个页面栈。
Home
push Detail
Home
↓
Detail
再 push Order
Home
↓
Detail
↓
Order
返回(goBack)
Home
↓
Detail
常见API
push
每次都会创建新页面
navigation.push("Detail");
navigation.push("Detail");
栈
Home
Detail
Detail
即使页面一样,也会创建。
navigate
如果页面不存在
创建
Home
↓
Detail
如果已经存在
直接跳过去
不会重复创建
navigation.navigate("Detail");
所以:
push
一定新增页面。
navigate
优先复用。
replace
替换当前页面
Home
↓
Login
replace
Home
↓
Main
Login 被销毁。
navigation.replace("Main");
常用于
登录成功
Splash
欢迎页
pop
返回N层
navigation.pop(2);
Home
↓
A
↓
B
↓
C
pop(2)
Home
↓
A
popToTop
直接回首页
navigation.popToTop();
reset
重建整个导航栈
例如退出登录
navigation.reset({
index: 0,
routes: [{ name: "Login" }],
});
变成
Login
之前所有页面全部销毁。
二、页面生命周期
React Native 页面不是 Vue。
React Navigation 提供了一套 Navigation 生命周期。
主要:
mount
focus
blur
unmount
mount
第一次创建
useEffect(() => {
console.log("mount");
return () => {
console.log("unmount");
};
}, []);
focus
页面进入前台
import { useFocusEffect } from "@react-navigation/native";
useFocusEffect(
React.useCallback(() => {
console.log("focus");
return () => {
console.log("blur");
};
}, [])
);
什么时候触发?
Home
↓
Detail
进入 Detail
Home blur
Detail focus
返回
Detail blur
Home focus
useIsFocused
判断当前是否显示
const isFocused = useIsFocused();
useEffect(() => {
if (isFocused) {
loadData();
}
}, [isFocused]);
很多列表都会这样刷新。
三、页面销毁(Unmount)
很多人误解:
返回才销毁。
实际上:
只有真正移除 Stack 才销毁。
例如:
Home
↓
Detail
goBack
Detail
unmount
Home
不会重新 mount。
push
Home
↓
Detail
↓
Order
Order
mount
Detail
没有销毁
只是 blur。
所以:
Home
其实一直活着。
如何强制销毁页面?
replace
replace()
替换页面。
reset
重建导航。
条件渲染
有时直接不渲染 Stack。
例如:
{
isLogin
? <MainStack />
: <LoginStack />
}
切换时整个 Stack 全部销毁。
四、返回拦截
最常见:
编辑页面
退出提醒
beforeRemove
官方推荐
useEffect(() => {
const unsubscribe = navigation.addListener(
"beforeRemove",
(e) => {
e.preventDefault();
Alert.alert(
"提示",
"确定退出?",
[
{
text: "取消",
style: "cancel",
},
{
text: "确定",
onPress: () => navigation.dispatch(e.data.action),
},
]
);
}
);
return unsubscribe;
}, [navigation]);
所有返回都会拦截:
-
Header 返回
-
手势返回
-
goBack()
-
Android 返回键
Android BackHandler
安卓物理返回键
import { BackHandler } from "react-native";
useEffect(() => {
const backAction = () => {
Alert.alert("退出?");
return true;
};
const sub = BackHandler.addEventListener(
"hardwareBackPress",
backAction
);
return () => sub.remove();
}, []);
返回
true
表示
我处理了
系统不要返回。
返回
false
系统继续默认返回。
五、监听页面事件
除了 focus,还有:
navigation.addListener("focus", () => {})
navigation.addListener("blur", () => {})
navigation.addListener("state", () => {})
navigation.addListener("beforeRemove", () => {})
例如:
useEffect(() => {
const sub = navigation.addListener("focus", () => {
console.log("页面显示");
});
return sub;
}, []);
六、页面缓存机制
React Navigation 默认:
不会因为切换到其他页面就销毁当前页面。
例如:
Home
↓
Detail
↓
Order
内存中实际上:
Home
Detail
Order
只有:
pop
replace
reset
才真正销毁。
Tab Navigator 也是类似。
Home
Mine
Setting
切换:
Home
↓
Mine
Home 不会重新创建。
所以很多人会发现:
useEffect(() => {}, [])
只执行一次。
真正刷新应该使用
useFocusEffect()
七、实际项目中的最佳实践
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 页面首次加载数据 | useEffect(() => {}, []) |
只在首次挂载执行 |
| 每次返回页面刷新数据 | useFocusEffect 或 useIsFocused |
页面重新获得焦点时执行 |
| 登录成功进入首页 | replace 或 reset |
防止返回登录页 |
| 退出登录 | reset |
清空整个导航栈 |
| 编辑页面防止误返回 | beforeRemove |
可统一拦截所有返回方式 |
| Android 物理返回键 | BackHandler(必要时配合 beforeRemove) |
处理退出应用或自定义返回逻辑 |
| 返回上一页 | goBack() |
标准返回操作 |
| 连续打开同一页面 | push() |
每次创建新的页面实例 |
| 避免重复打开已有页面 | navigate() |
优先复用已有页面 |
面试高频问题
-
push和navigate有什么区别?-
push:始终创建新的页面实例。 -
navigate:若目标页面已存在(根据导航器行为),优先跳转到已有页面,避免重复创建。
-
-
页面切换时为什么
useEffect(() => {}, [])不会再次执行?- 因为页面通常没有被销毁,只是从
focus变为blur。再次显示时不会重新mount,因此应使用useFocusEffect或监听focus事件处理每次进入页面的逻辑。
- 因为页面通常没有被销毁,只是从
-
如何清空整个导航栈?
- 使用
navigation.reset(),重新设置导航状态。
- 使用
-
如何拦截返回操作?
- 推荐使用
beforeRemove统一拦截导航返回;若需要处理 Android 物理返回键,可结合BackHandler使用。
- 推荐使用
掌握这些内容,基本覆盖了 React Native 路由管理在日常开发和中高级前端面试中的核心知识点。