当前位置: 首页 > ai >正文

记录一次react渲染优化

一、需要解决的问题

1.首次加载缓慢;
2.修改表单时,有时会修改失败。

二、问题分析

1.使用react-scan插件查看加载慢的问题,发现首次进入页面的时候会页面全屏重复加载多次的问题,每次修改表单内容都会触发整个应用的重新渲染。

2.重复的React.useEffect订阅,有多个useEffect同时订阅store的变化。

3.更新函数触发连锁反应,每当表单项发生变化时, `updateItem` 会更新areas,这触发了areas订阅,由于订阅的store是绑定的全局app.store所以会造成全局渲染。

三、问题解决

1.  优化订阅逻辑,减少不必要的更新触发
// 修改前
React.useEffect(() =>store.subscribe((s) => s.areas,(areas) => {if (!task || step !== 1) returnconsole.log('areas changed')updateTaskThrottledCallback({ ...task, areas })}),[task, step])React.useEffect(() =>store.subscribe((s) => s.roiTransformer,(roiTransformer) => {if (!task || step !== 1) returnconsole.log('roiTransformer changed')updateTaskThrottledCallback({ ...task, roiTransformer })}),[task, step])React.useEffect(() =>store.subscribe((s) => s.imagePreprocessing,(imagePreprocessing) => {if (!task || step !== 1) returnconsole.log('imagePreprocessing changed')updateTaskThrottledCallback({ ...task, imagePreprocessing })}),[task, step])React.useEffect(() => {if (!device) returnsetConfiguringStep(device.id, step)setTaskName(task?.name ?? '')}, [step])// 修改后React.useEffect(() =>store.subscribe((s) => ({ areas: s.areas, roiTransformer: s.roiTransformer, imagePreprocessing: s.imagePreprocessing }),({ areas, roiTransformer, imagePreprocessing }) => {if (!task || step !== 1) return// 添加深度比较,避免因为对象引用变化而触发不必要的更新const hasRealChanges = JSON.stringify(task.areas) !== JSON.stringify(areas) ||JSON.stringify(task.roiTransformer) !== JSON.stringify(roiTransformer) ||JSON.stringify(task.imagePreprocessing) !== JSON.stringify(imagePreprocessing)if (!hasRealChanges) returnconsole.log('task data changed')updateTaskThrottledCallback({ ...task, areas, roiTransformer, imagePreprocessing })},{equalityFn: (a, b) =>a.areas === b.areas &&a.roiTransformer === b.roiTransformer &&a.imagePreprocessing === b.imagePreprocessing}),[task, step])
2. 拆解store

使用一个对应模块的store去拆解全局的app.store或者新建一个临时的store,在合适的时机(如点击完成按钮),去更新到全局的store.

3. 表单组件的优化

使用 useCallback 包裹了 handleItemChecked 、 handleItemChanged 和 handleFormatChange 等更新方法.

useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。useCallback – React 中文文档

memo 允许你的组件在 props 没有改变的情况下跳过重新渲染。memo – React 中文文档

使用Memo包裹子组件

const Item = React.memo(({ item, itemIndex, handleItemChanged, handleFormatChange }) => {// 组件逻辑
})
4. 事件处理函数优化
// 优化前
const handleItemChecked = (itemIndex: number, checked: boolean) => {// 直接更新逻辑
}// 优化后
const handleItemChecked = useCallback((itemIndex: number, checked: boolean) => {const item = tool.config.form.items[itemIndex]if (item.enabled === checked) return // 避免重复更新const updatedItem = { ...item, enabled: checked }tempFormStore.getState().addPendingUpdate(areaIndex, toolIndex, itemIndex, updatedItem)
}, [tool.config.form.items, areaIndex, toolIndex])
5. 订阅逻辑优化

缺乏深度比较,对象引用变化就会触发更新

没有检查数据是否真正发生变化

// 优化前
React.useEffect(() =>store.subscribe((s) => ({ areas: s.areas, roiTransformer: s.roiTransformer, imagePreprocessing: s.imagePreprocessing }),({ areas, roiTransformer, imagePreprocessing }) => {if (!task || step !== 1) returnconsole.log('task data changed')updateTaskThrottledCallback({ ...task, areas, roiTransformer, imagePreprocessing })}),[task, step]
)// 优化后
React.useEffect(() =>store.subscribe((s) => ({ areas: s.areas, roiTransformer: s.roiTransformer, imagePreprocessing: s.imagePreprocessing }),({ areas, roiTransformer, imagePreprocessing }) => {if (!task || step !== 1) return// 添加深度比较,避免因为对象引用变化而触发不必要的更新const hasRealChanges = JSON.stringify(task.areas) !== JSON.stringify(areas) ||JSON.stringify(task.roiTransformer) !== JSON.stringify(roiTransformer) ||JSON.stringify(task.imagePreprocessing) !== JSON.stringify(imagePreprocessing)if (!hasRealChanges) returnconsole.log('task data changed')updateTaskThrottledCallback({ ...task, areas, roiTransformer, imagePreprocessing })}),[task, step]
)
6. 区域组件渲染优化

乏精确的比较函数,自定义比较函数,只在真正需要更新时才重新渲染

const AreaItem = React.memo(({ areaIndex, area, onShowPanel }) => {// 组件逻辑
}, (prevProps, nextProps) => {// 自定义比较函数,只在真正需要更新时才重新渲染return (prevProps.areaIndex === nextProps.areaIndex &&prevProps.area.tools.length === nextProps.area.tools.length &&prevProps.area.points.length === nextProps.area.points.length &&JSON.stringify(prevProps.area.tools) === JSON.stringify(nextProps.area.tools) &&JSON.stringify(prevProps.area.points) === JSON.stringify(nextProps.area.points))
})

http://www.xdnf.cn/news/17610.html

相关文章:

  • 实现文字在块元素中水平/垂直居中详解
  • 教程 | 用Parasoft SOAtest实现高效CI回归测试
  • AWS EKS 常用命令大全:从基础管理到高级运维
  • [激光原理与应用-257]:理论 - 几何光学 - 光束整形
  • Springboot注册过滤器的三种方式(Order 排序)
  • Spring Cloud系列—Config配置中心
  • 【Oracle APEX开发小技巧16】交互式网格操作内容根据是否启用进行隐藏/展示
  • VS4210芯片技术资料(IT6604+VS4210+MDIN380连接原理图)
  • 基于STC8单片机的RTC时钟实现:从原理到实践
  • 如何使股指期货套期保值效果更加精准?
  • Ansible部署应用
  • AR巡检:三大核心技术保障数据准确性
  • 聚合搜索中的设计模式
  • 【Unity】Unity中ContentSizeFitter有时无法及时自适应大小问题解决
  • Baumer高防护相机如何通过YoloV8深度学习模型实现木板表面缺陷的检测识别(C#代码UI界面版)
  • python --- 基础语法(1)
  • Web 开发 14
  • [SC]如何使用sc_semaphore实现对共享资源的访问控制
  • 【网络运维】Linux和自动化:Ansible
  • 基于RAII的智能指针原理和模拟实现智能指针
  • 企业培训笔记:宠物信息管理--实现宠物信息的添加
  • NLP—词向量转换评论学习项目分析
  • 【Java项目与数据库、Maven的关系详解】
  • Docker部署kafka实操+Java中访问
  • 42.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--扩展功能--集成网关--网关集成认证(一)
  • 云计算概述
  • 【web站点安全开发】任务2:HTML5核心特性与元素详解
  • GitLab CI + Docker 自动构建前端项目并部署 — 完整流程文档
  • 跨界重构规则方法论
  • TCP服务器网络编程设计流程详解