

子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)
大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。
我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案,
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。
技术方向: 前端 / 跨端 / 小程序 / 移动端工程化 内容平台: 掘金、知乎、CSDN、简书 创作特点: 实战导向、源码拆解、少空谈多落地 **文章状态:**长期稳定更新,大量原创输出
我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在"API 怎么用",而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。
子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源 (工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学"明白",也用"到位"
持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱
文章目录
-
- 为什么你需要一张"状态扩散图"
- 什么是"状态扩散"
- 最危险的"全列表扩散型"
- [Context 驱动的"隐形全量扩散"](#Context 驱动的“隐形全量扩散”)
- [Redux 的"选择器扩散型"](#Redux 的“选择器扩散型”)
- 可控的"局部扩散型"
- [Zustand / Jotai 的"精准订阅型"](#Zustand / Jotai 的“精准订阅型”)
- 把图连起来:你现在在哪一层?
- 真实项目中的一个判断方法
- 总结
为什么你需要一张"状态扩散图"
在 RN 项目里,很多性能问题其实都有一个共同特点:
- 不好复现
- 不好量化
- 不好定位
尤其是列表场景,经常出现这种情况:
数据不多
手机性能也不差
偶尔就是掉一帧
这类问题,如果你只靠:
- "加 memo"
- "改 FlatList 参数"
- "试试看能不能顺一点"
基本等于蒙。
真正有效的方式,是在写代码之前就判断:
这个状态更新,会扩散到哪里?
而"状态扩散图",就是把这个问题一次性说明白的工具。
什么是"状态扩散"
先别急着画图,我们先把概念说清楚。
状态扩散不是"状态变多"
很多人以为:
状态多 = 性能差
这是不对的。
真正危险的是:
一个状态更新,会影响多少组件参与 rerender
这就是"扩散半径"。
一个极简定义
你可以把它记成一句话:
状态扩散 = 一次 setState,引发的组件重渲染路径
路径越长、覆盖面越大,性能风险越高。
最危险的"全列表扩散型"
我们先从最常见、也是最危险的一种状态模型开始。
代码结构
tsx
function ListPage() {
const [likedMap, setLikedMap] = useState({})
return (
<FlatList
data={data}
renderItem={({ item }) => (
<Item liked={likedMap[item.id]} />
)}
/>
)
}
状态扩散图
setLikedMap
↓
ListPage rerender
↓
FlatList rerender
↓
renderItem(全部)
↓
Item(全部)
风险评估
- 扩散起点:Item 级交互
- 扩散终点:整个列表
- 扩散层级:4 层
- 扩散数量:N(列表长度)
结论只有一句话:
这是一个"必卡型"结构,只是早晚问题。
Context 驱动的"隐形全量扩散"
这一种,比第一种更容易被忽略。
看起来很"架构化"的写法
tsx
<ListContext.Provider value={listState}>
<FlatList ... />
</ListContext.Provider>
tsx
function Item() {
const { likedMap } = useContext(ListContext)
}
状态扩散图
Context value 更新
↓
所有 Consumer rerender
↓
所有 Item rerender
为什么它特别危险
因为:
- Context 更新没有 selector
- 不管你用不用那一项数据
- 只要 value 引用变了,必 rerender
你会发现一个很残酷的事实:
Context 在列表里,几乎等价于"广播状态"
Redux 的"选择器扩散型"
Redux 经常被误伤,说它"慢",其实问题不在 Redux 本身。
典型写法
tsx
const likedMap = useSelector(state => state.likedMap)
状态扩散图
dispatch
↓
store 更新
↓
selector 返回新引用
↓
订阅组件 rerender
如果订阅发生在:
- ListPage
- renderItem 内
- Item 内但 selector 粒度过大
那结果就是:
Redux 版本的"全列表扩散"
可控的"局部扩散型"
现在来看一张健康的状态扩散图。
Item 自持状态
tsx
const Item = React.memo(({ item }) => {
const [liked, setLiked] = useState(false)
})
状态扩散图
Item setState
↓
当前 Item rerender
风险评估
- 扩散起点:Item
- 扩散终点:Item
- 扩散层级:1
- 扩散数量:1
这是 RN 列表里最理想的状态模型。
Zustand / Jotai 的"精准订阅型"
这类状态管理工具,在 RN 列表里表现好的原因,本质也在这里。
精准订阅示例
tsx
const liked = useStore(state => state.liked[item.id])
状态扩散图
store 更新
↓
命中 selector
↓
当前 Item rerender
关键点不在"快",在"窄"
- 不是 store 更新小
- 是 rerender 命中范围小
把图连起来:你现在在哪一层?
你可以把 RN 列表里的状态模型,直接分成这几档:
Level 1:Item 局部状态
Level 2:Item 精准订阅 store
Level 3:ListPage 局部状态
Level 4:Context 广播状态
Level 5:Redux 大对象订阅
越往下:
- 状态扩散半径越大
- 性能风险指数级上升
真实项目中的一个判断方法
以后你看任何一个 RN 列表,只问自己 3 个问题:
- 这个状态更新,最先 rerender 的组件是谁?
- rerender 会不会回到 FlatList 这一层?
- renderItem 会不会因此全量执行?
只要有一个答案是"会",你就已经可以预判:
这个列表,一定会在未来某个需求下卡。
总结
RN 列表性能问题,真正的分水岭不是:
- 用不用 FlatList
- 用不用 memo
- 用不用高端手机
而是:
你有没有在脑子里,提前画过一张"状态扩散图"
一旦你开始用这种方式看代码:
- 性能问题会提前暴露
- 架构问题会提前规避
- 优化不再是救火,而是设计的一部分