RN 路由栈管理、页面销毁、返回拦截

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

即使页面一样,也会创建。


如果页面不存在

创建

复制代码
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(() => {}, []) 只在首次挂载执行
每次返回页面刷新数据 useFocusEffectuseIsFocused 页面重新获得焦点时执行
登录成功进入首页 replacereset 防止返回登录页
退出登录 reset 清空整个导航栈
编辑页面防止误返回 beforeRemove 可统一拦截所有返回方式
Android 物理返回键 BackHandler(必要时配合 beforeRemove 处理退出应用或自定义返回逻辑
返回上一页 goBack() 标准返回操作
连续打开同一页面 push() 每次创建新的页面实例
避免重复打开已有页面 navigate() 优先复用已有页面

面试高频问题

  1. pushnavigate 有什么区别?

    • push:始终创建新的页面实例。

    • navigate:若目标页面已存在(根据导航器行为),优先跳转到已有页面,避免重复创建。

  2. 页面切换时为什么 useEffect(() => {}, []) 不会再次执行?

    • 因为页面通常没有被销毁,只是从 focus 变为 blur。再次显示时不会重新 mount,因此应使用 useFocusEffect 或监听 focus 事件处理每次进入页面的逻辑。
  3. 如何清空整个导航栈?

    • 使用 navigation.reset(),重新设置导航状态。
  4. 如何拦截返回操作?

    • 推荐使用 beforeRemove 统一拦截导航返回;若需要处理 Android 物理返回键,可结合 BackHandler 使用。

掌握这些内容,基本覆盖了 React Native 路由管理在日常开发和中高级前端面试中的核心知识点。

相关推荐
Heo5 小时前
大厂前端调试不能只会debugger
前端·javascript·面试
想要成为糕糕手6 小时前
🚀 在浏览器里跑 DeepSeek-R1?WebGPU 端侧推理实战(六)—— 完结篇:消息渲染与流式收尾
react.js·typescript·llm
windliang7 小时前
Claude Code 源码分析(十二):错误处理与自动恢复:让 Agent 稳定运行
前端·javascript·面试
默_笙7 小时前
🛬 前端路由的"高级玩法":懒加载、404、鉴权路由,一个都不能少(下篇)
前端·javascript
sunly_7 小时前
TypeScript:3、类型声明与类型推断
javascript·ubuntu·typescript
YIAN7 小时前
从 Hash 底层原理到 React Router v6 实战:我学会了什么?
前端·react.js·vue-router
奥莱维8 小时前
【无标题】
java·前端·javascript
To_OC8 小时前
后端接口还没交付,前端如何独立把整套业务跑通
前端·react.js·全栈
黄金决明子9 小时前
浏览器Window底层操作全解
前端·javascript
程序员跑路10 小时前
Barrel Export、Jest、Babel 与依赖图
react.js