2026/1/10 2:37:26
网站建设
项目流程
怎么用自己的电脑做网站主机,asp做网站简介页面,想学网页设计报考什么专业,做网站设计提成赚钱吗[toc]
为什么这是一类“怎么优化都没用”的问题
RN 列表性能问题里#xff0c;有一类非常让人崩溃的场景#xff1a;你已经#xff1a;
用了 React.memo用了 useCallback控制了 keyExtractor甚至拆了子组件但#xff1a;
点一个按钮#xff0c;列表还是会卡滑动时偶发掉帧…[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 不变的 rerenderconst 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] useStateRecordstring, 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 的优化参数救不了你很多人会继续尝试initialNumToRenderwindowSizeremoveClippedSubviews但这些参数解决的是“一次渲染多少 item”而不是“为什么会触发渲染”当渲染触发点在 ListPage 时FlatList 已经没有选择权了它只能老老实实重新算一遍 renderItem真正有效的分界线状态的“最小归属单位”所有性能优化是否有效只取决于一件事状态变化能不能被限制在最小使用单元内不可优化的模型状态 → ListPage任何变化影响整个列表。可优化的模型状态 → Item变化只影响自己。把状态“拆散”为什么性能突然就好了我们直接对比两种写法。集中式不可扩展Item liked{likedMap[item.id]} /分散式可扩展const Item React.memo(({ item }) { const [liked, setLiked] useState(false) })此时渲染链路变成Item setState → 当前 Item rerenderListPage 不动FlatList 不动其他 item 完全无感你会发现一个非常明显的变化甚至不需要 memo性能就已经很好了Redux / Context 为什么会“让一切优化失效”Redux 的问题不是慢而是“订阅面太大”useSelector(state state.list)任何 list 内字段变化selector 返回新引用所有订阅组件 rerender在列表场景下这几乎等价于每次交互 全列表 rerenderContext 是最容易被低估的性能杀手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 参数会失效你只剩下“体感卡顿”