双河市网站建设_网站建设公司_跨域_seo优化
2026/1/3 10:32:49 网站建设 项目流程

@[toc]

为什么这是一类“怎么优化都没用”的问题

RN 列表性能问题里,有一类非常让人崩溃的场景:

  • 你已经:

    • 用了React.memo
    • 用了useCallback
    • 控制了keyExtractor
    • 甚至拆了子组件
  • 但:

    • 点一个按钮,列表还是会卡
    • 滑动时偶发掉帧
    • 性能分析一看,renderItem 还是在狂跑

很多人会下意识得出一个结论:

FlatList 不行
RN 性能差
JS 线程太慢

但如果你回头看这些项目,80% 都有一个共同点

状态被集中管理了。

而且集中得“非常合理”。

什么叫“状态一集中”

先说清楚概念。

典型的“状态集中”长这样

function ListPage() { const [listState, setListState] = useState({ likedMap: {}, selectedMap: {}, expandedMap: {}, }) return ( <FlatList data={data} renderItem={({ item }) => ( <Item item={item} liked={listState.likedMap[item.id]} selected={listState.selectedMap[item.id]} /> )} /> ) }

业务角度看,这段代码没有任何问题:

  • 状态统一
  • 数据集中
  • 管理方便

但从渲染模型看,它已经埋下了一颗性能炸弹。

集中的不是“数据”,而是“影响范围”

真正的问题不在于你“把状态放一起了”,而在于:

任何一个状态变更,影响的最小单位是整个 ListPage

也就是说:

任意一个 item 的交互 → ListPage rerender → FlatList rerender → 所有 renderItem 重新执行

这条链路一旦成立,后面所有优化都会变成装饰品

为什么 memo / useCallback 在这里几乎没用

这是很多人最困惑的地方。

React.memo 只能挡 props 不变的 rerender

const Item = React.memo(({ liked }) => { ... })

看起来好像能挡住重渲染,对吧?

但问题在于:

  • liked是从listState解构出来的
  • 每次setListStatelistState都是一个新对象
  • renderItem 每次都会重新执行

结果是:

memo 挡住了 Item 的 render 函数,但挡不住 renderItem 本身

而 renderItem 恰恰是 FlatList 最重的一层。

useCallback 只能解决“函数引用”,解决不了“依赖变化”

const onLike = useCallback(() => { ... }, [listState])

只要你依赖的是集中状态对象

  • callback 每次都会重新创建
  • 下游组件依然会 rerender

你会发现一个很讽刺的现象:

状态越集中,useCallback 的 dependency 越大,越没意义

一次 state 更新,是如何引发“渲染雪崩”的

这一节我们用一个非常具体的 Demo,把链路拆清楚。

Demo:一个点赞引发的全列表重算

function ListPage() { const [likedMap, setLikedMap] = useState<Record<string, boolean>>({}) const toggleLike = (id: string) => { setLikedMap(prev => ({ ...prev, [id]: !prev[id], })) } return ( <FlatList data={data} renderItem={({ item }) => { console.log('render item', item.id) return ( <Item item={item} liked={likedMap[item.id]} onLike={() => toggleLike(item.id)} /> ) }} /> ) }

点任意一个 item,你会在控制台看到:

render item 1 render item 2 render item 3 ... render item 100

哪怕你只点了第 57 个。

真正发生的事情(不是你以为的)

你以为发生的是:

第 57 个 item 状态更新 → 第 57 个 item rerender

但实际上发生的是:

setLikedMap → ListPage rerender → FlatList rerender → renderItem 全量执行 → JS 线程瞬间被占满

这就是渲染扩散

为什么 FlatList 的优化参数救不了你

很多人会继续尝试:

  • initialNumToRender
  • windowSize
  • removeClippedSubviews

但这些参数解决的是:

“一次渲染多少 item”

而不是:

“为什么会触发渲染”

当渲染触发点在 ListPage 时:

  • FlatList 已经没有选择权了
  • 它只能老老实实重新算一遍 renderItem

真正有效的分界线:状态的“最小归属单位”

所有性能优化是否有效,只取决于一件事:

状态变化,能不能被限制在最小使用单元内

不可优化的模型

状态 → ListPage

任何变化,影响整个列表。

可优化的模型

状态 → Item

变化只影响自己。

把状态“拆散”,为什么性能突然就好了

我们直接对比两种写法。

集中式(不可扩展)

<Item liked={likedMap[item.id]} />

分散式(可扩展)

const Item = React.memo(({ item }) => { const [liked, setLiked] = useState(false) })

此时渲染链路变成:

Item setState → 当前 Item rerender
  • ListPage 不动
  • FlatList 不动
  • 其他 item 完全无感

你会发现一个非常明显的变化:

甚至不需要 memo,性能就已经很好了

Redux / Context 为什么会“让一切优化失效”

Redux 的问题不是慢,而是“订阅面太大”

useSelector(state => state.list)

任何 list 内字段变化:

  • selector 返回新引用
  • 所有订阅组件 rerender

在列表场景下,这几乎等价于:

每次交互 = 全列表 rerender

Context 是最容易被低估的性能杀手

<ListContext.Provider value={listState}>

Context 的规则非常简单粗暴:

value 变了,所有 consumer 必须更新

在列表里,这意味着:

Context 更新 = renderItem 全跑

为什么“状态集中”在 Web 里没这么致命

这是一个很关键的对比点。

在 Web 里:

  • DOM diff 有天然的兜底
  • 浏览器渲染线程很强
  • 局部 repaint 成本低

但在 RN 里:

  • JS → Shadow Tree → Native
  • 每一次 rerender 都是跨线程协作
  • JS 线程一旦被拖慢,滑动立刻掉帧

所以:

RN 对“状态扩散”是零容忍的

总结

RN 里所有性能优化是否生效,只取决于一件事:
状态变化能不能被限制在足够小的范围内

一旦状态被集中:

  • memo 会失效
  • useCallback 会失效
  • FlatList 参数会失效
  • 你只剩下“体感卡顿”

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询