
网罗开发 (小红书、快手、视频号同名)
大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。
图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG
我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验 。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。
展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索"展菲",即可纵览我在各大平台的知识足迹。
📣 公众号"Swift社区",每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友"fzhanfei",与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!
文章目录
-
- 摘要
- [为什么很多 RN 开发者"学过 Navigation,但还是用错了"](#为什么很多 RN 开发者“学过 Navigation,但还是用错了”)
- 先建立一个总览:三层生命周期同时存在
-
- [第一层:React 组件生命周期](#第一层:React 组件生命周期)
- [第二层:Navigation 页面生命周期](#第二层:Navigation 页面生命周期)
- 第三层:业务生命周期(用户视角)
- 核心结论先给出
- 从一个最常见的页面跳转说起
- 时间轴拆解:一次完整的页面往返
-
- [第一次进入 Home](#第一次进入 Home)
- [从 Home 跳转到 Detail](#从 Home 跳转到 Detail)
- [从 Detail 返回 Home](#从 Detail 返回 Home)
- 用一张"心智模型表"记住它
- [为什么 useEffect 在 Navigation 中经常"失灵"](#为什么 useEffect 在 Navigation 中经常“失灵”)
- [正确的心智转变:页面 = 有状态的长生命周期对象](#正确的心智转变:页面 = 有状态的长生命周期对象)
- [什么时候该用 useEffect,什么时候该用 useFocusEffect](#什么时候该用 useEffect,什么时候该用 useFocusEffect)
-
- [适合 useEffect 的场景](#适合 useEffect 的场景)
- [必须用 useFocusEffect 的场景](#必须用 useFocusEffect 的场景)
- 一个完整的可运行示例:对比两种写法
-
- [错误示例:useEffect 管理页面请求](#错误示例:useEffect 管理页面请求)
- [正确示例:useFocusEffect 管理页面请求](#正确示例:useFocusEffect 管理页面请求)
- [unmountOnBlur 是"银弹"吗?](#unmountOnBlur 是“银弹”吗?)
- 一个判断标准:问自己三个问题
- 总结
摘要
在 React Navigation 里,生命周期问题几乎是所有复杂 Bug 的源头。
很多看起来"很诡异"的现象,本质上都能归结为一句话:
你以为页面已经销毁了,但它其实还活着
你以为 useEffect 会重新跑,但它其实根本没机会
这篇文章不打算从 API 一个个列,而是从心智模型出发,帮你真正建立三件事的统一认知:
- 组件生命周期(React)
- 页面生命周期(Navigation)
- 业务生命周期(用户视角)
当这三层对齐之后,你会发现:
- 页面切换不再"玄学"
- 副作用管理变得非常清晰
- 很多历史遗留 Bug 一下子就能解释通了
为什么很多 RN 开发者"学过 Navigation,但还是用错了"
先说一个非常真实的现象。
你可能:
- 用过
useEffect - 用过
useFocusEffect - 也知道有
focus、blur
但当遇到下面这些问题时,还是会懵:
- 为什么返回页面后数据没刷新?
- 为什么某个请求跑了两次?
- 为什么定时器越跑越多?
- 为什么 setState 报错组件已卸载,但我明明还能看到页面?
原因只有一个:
你脑子里只有"组件生命周期",没有"页面生命周期模型"。
先建立一个总览:三层生命周期同时存在
在 RN + React Navigation 中,你必须同时理解三套生命周期。
第一层:React 组件生命周期
这是大家最熟的:
- mount
- update
- unmount
- useEffect / cleanup
它只关心一件事:组件在不在内存里。
第二层:Navigation 页面生命周期
这是最容易被忽略的:
- focus
- blur
- transition
- stack 中的存在状态
它关心的是:页面是否处于"当前活跃状态"。
第三层:业务生命周期(用户视角)
这是最容易被写错的:
- 用户是否正在看这个页面
- 用户是否关心当前副作用
- 当前逻辑是否"还有意义"
绝大多数副作用,其实属于这一层。
核心结论先给出
在进入细节之前,先给你三条"底层规则",后面所有内容都会围绕它展开。
- 页面 blur ≠ 组件 unmount
- useEffect 默认只和 mount / unmount 有关
- 绝大多数业务副作用,应该绑定 focus / blur,而不是 mount
如果你把这三条真正吃透,80% 的 Navigation Bug 都会消失。
从一个最常见的页面跳转说起
我们从最典型的 Stack Navigation 开始。
txt
Home → Detail → Profile
实际发生了什么(默认配置)
当你从 Home 跳到 Detail:
- Home:
blur - Detail:
mount + focus - Home:仍然 mounted
再从 Detail 跳到 Profile:
- Detail:
blur - Profile:
mount + focus - Home、Detail:全部还在内存里
这一步,是很多人理解错误的起点。
时间轴拆解:一次完整的页面往返
我们用 Home → Detail → Home 举例。
第一次进入 Home
txt
Home mount
Home focus
useEffect(() => {}, [])执行useFocusEffect执行
从 Home 跳转到 Detail
txt
Home blur
Detail mount
Detail focus
注意这一步:
- Home 没有 unmount
- Home 的
useEffect cleanup不会执行
从 Detail 返回 Home
txt
Detail blur
Home focus
这里是高频踩坑点:
- Home 不会重新 mount
useEffect(() => {}, [])不会再跑- 只有 focus 相关逻辑会触发
用一张"心智模型表"记住它
| 行为 | useEffect([]) | useFocusEffect | 组件是否存在 |
|---|---|---|---|
| 首次进入页面 | 执行 | 执行 | 是 |
| 跳转到其他页面 | 不触发 | cleanup | 是 |
| 返回页面 | 不执行 | 再次执行 | 是 |
| 页面被真正销毁 | cleanup | cleanup | 否 |
这张表,建议你直接刻进脑子里。
为什么 useEffect 在 Navigation 中经常"失灵"
很多人会写这样的代码:
tsx
useEffect(() => {
fetchData();
}, []);
然后发现:
- 页面第一次进来 OK
- 返回页面后数据不刷新
- 只能靠手动刷新兜底
问题不是 useEffect 有 bug,而是你把"页面激活"当成了"组件创建"。
在 Navigation 世界里:
页面重新可见 ≠ 组件重新创建
正确的心智转变:页面 = 有状态的长生命周期对象
你应该把一个 Screen 理解为:
一个长期存在的对象,在不同时间段被 focus / blur
而不是:
每次进入就 new 一个,用完就销毁
一旦你接受了这个模型,很多设计会自然发生变化。
什么时候该用 useEffect,什么时候该用 useFocusEffect
这是最实用的问题,没有之一。
适合 useEffect 的场景
这些逻辑只和组件存在有关:
- 初始化静态配置
- 注册全局工具(一次即可)
- 只需要执行一次的逻辑
tsx
useEffect(() => {
initAnalytics();
}, []);
必须用 useFocusEffect 的场景
这些逻辑和页面是否可见强相关:
- 页面级数据请求
- 轮询 / 定时器
- WebSocket 订阅
- 页面埋点曝光
- 动画启动 / 停止
tsx
useFocusEffect(
useCallback(() => {
startPolling();
return stopPolling;
}, [])
);
一个完整的可运行示例:对比两种写法
错误示例:useEffect 管理页面请求
tsx
useEffect(() => {
console.log('fetch data');
fetchData();
}, []);
表现:
- 只在第一次进入时请求
- 返回页面数据不更新
正确示例:useFocusEffect 管理页面请求
tsx
useFocusEffect(
useCallback(() => {
console.log('fetch data on focus');
fetchData();
}, [])
);
表现:
- 每次页面可见都会触发
- 行为符合用户直觉
unmountOnBlur 是"银弹"吗?
很多人会问:
我直接把页面 blur 时 unmount 不就完了?
tsx
options={{ unmountOnBlur: true }}
它能解决什么?
- 副作用自动清理
- useEffect 行为更"像直觉"
它带来的代价
- 页面状态完全丢失
- 重进页面性能开销变大
- 动画 / 滚动位置无法保留
结论是:
它是工具,不是默认答案
大多数情况下,你需要的是:
- 正确管理 focus 生命周期
- 而不是强行销毁页面
一个判断标准:问自己三个问题
每次写副作用前,问自己:
- 页面不可见时,这个逻辑还有意义吗?
- 用户切走页面后,我是否还关心这个结果?
- 返回页面时,是否应该"重新激活"它?
如果答案偏向"是 / 否",你就知道该选哪种生命周期了。
总结
React Navigation 的生命周期并不复杂,复杂的是我们用错了抽象层级。
你真正需要记住的只有一句话:
组件生命周期决定"在不在",页面生命周期决定"该不该跑"。
当你把:
- useEffect → 组件存在
- useFocusEffect → 页面可见
这条边界画清楚之后:
- 页面逻辑会变得非常稳定
- Bug 数量会明显下降
- 项目越大,收益越明显