You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何降低React Recharts柱状图3.4万数据点的CPU占用?

大数据量Recharts柱状图虚拟化优化方案

核心逻辑

虚拟化的核心是只渲染当前视口内的柱子+少量缓冲区域,避免一次性渲染34000个DOM节点拖垮性能。具体逻辑:

  1. 计算视口可容纳的柱子数量,结合缓冲值确定需要渲染的数据范围
  2. 监听容器滚动/拖拽事件,实时更新可视数据的起始、结束索引
  3. 截取对应数据片段传给Recharts,同时模拟完整的X轴刻度与布局,保证滚动时视觉连贯

具体实现步骤

1. 基础配置与状态定义

先定义关键状态与常量,控制渲染范围:

import { useState, useEffect, useRef } from 'react';
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer } from 'recharts';

// 常量配置
const BAR_WIDTH = 12; // 单个柱子宽度(含间距)
const BUFFER_COUNT = 10; // 前后缓冲的柱子数量,避免滚动时出现空白
const CONTAINER_WIDTH = 800; // 可视容器宽度

const VirtualizedBarChart = ({ originalData, predefinedColors }) => {
  const [startIndex, setStartIndex] = useState(0);
  const [endIndex, setEndIndex] = useState(Math.floor(CONTAINER_WIDTH / BAR_WIDTH) + BUFFER_COUNT * 2);
  const scrollContainerRef = useRef(null);
  const totalBars = originalData.length;
  const totalWidth = totalBars * BAR_WIDTH;

  // 后续逻辑...
};

2. 监听滚动,更新渲染范围

监听容器滚动事件,计算当前可视区域对应的起始索引:

useEffect(() => {
  const handleScroll = () => {
    if (!scrollContainerRef.current) return;
    const scrollLeft = scrollContainerRef.current.scrollLeft;
    // 计算起始索引,减去缓冲值避免边缘空白
    const newStart = Math.max(0, Math.floor(scrollLeft / BAR_WIDTH) - BUFFER_COUNT);
    // 计算结束索引,加上缓冲值
    const newEnd = Math.min(totalBars, newStart + Math.floor(CONTAINER_WIDTH / BAR_WIDTH) + BUFFER_COUNT * 2);
    setStartIndex(newStart);
    setEndIndex(newEnd);
  };

  const container = scrollContainerRef.current;
  container?.addEventListener('scroll', handleScroll);
  return () => container?.removeEventListener('scroll', handleScroll);
}, [totalBars]);

3. 截取可视数据,适配X轴布局

截取需要渲染的数据片段,并自定义X轴确保刻度显示完整范围:

// 截取当前可视区域的数据(含缓冲)
const visibleData = originalData.slice(startIndex, endIndex);

// 自定义X轴刻度,基于原始数据的索引生成,避免刻度重叠
const customXAxisTicks = () => {
  const tickStep = Math.ceil(totalBars / 10); // 控制显示约10个刻度
  return Array.from({ length: Math.ceil(totalBars / tickStep) }, (_, i) => {
    const index = i * tickStep;
    return { value: index, label: originalData[index]?.x || index };
  });
};

4. 渲染虚拟化图表

结合你现有代码,渲染只包含可视区域的柱子,同时设置容器总宽度模拟完整图表布局:

return (
  <div 
    ref={scrollContainerRef}
    style={{ 
      width: '100%', 
      overflowX: 'auto', 
      maxWidth: CONTAINER_WIDTH 
    }}
  >
    <div style={{ width: `${totalWidth}px` }}>
      <ResponsiveContainer width="100%" height={400}>
        <BarChart
          data={visibleData}
          margin={{ top: 20, right: 30, left: 0, bottom: 5 }}
          barSize={BAR_WIDTH - 2} // 柱子实际宽度,留出间距
          animation={false} // 大数据量下关闭动画提升性能
        >
          <XAxis
            dataKey="x"
            ticks={customXAxisTicks()}
            tickFormatter={(value) => originalData[value]?.x || value}
            domain={[0, totalBars - 1]} // 固定X轴范围,避免刻度随可视数据变化
          />
          <YAxis />
          {/* 复用你的循环渲染Bar逻辑,仅渲染可视数据的系列 */}
          {Object.keys(originalData[0]).filter(key => key !== 'x').map((key, index) => (
            <Bar
              key={key}
              dataKey={key}
              shape={<RoundedTopBar />}
              fill={predefinedColors[index % predefinedColors.length]}
            />
          ))}
        </BarChart>
      </ResponsiveContainer>
    </div>
  </div>
);

额外优化建议

  • 防抖滚动监听:给handleScroll加防抖函数,避免滚动时频繁触发状态更新
  • 合并系列渲染:如果系列数量过多,可合并相似系列,进一步减少DOM节点
  • 懒加载数据:如果数据来自接口,可分段加载,无需一次性获取全部34000条数据

内容的提问来源于stack exchange,提问作者Lokesh Kumar Bairwa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 22:07:23