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

AG-Grid水平滚动时如何稳定一致地自动调整虚拟列宽

AG-Grid 虚拟化表格动态列宽自动调整优化方案

问题根因

现有实现效果不稳定的核心原因有三点:

  • 监听的processRowPostCreate、onVirtualColumnsChanged事件触发时机早于虚拟列DOM的最终渲染,此时调用autoSizeColumns无法获取单元格真实内容宽度,计算结果存在偏差
  • 水平滚动时onVirtualColumnsChanged触发频率和滚动帧率一致,同步执行列宽计算会频繁触发表格重排,和渲染线程抢占资源,导致滚动卡顿、计算逻辑跟不上滚动速度
  • 首屏初始化阶段没有在数据首次渲染完成后触发计算,导致首次加载时列宽不会自动调整

优化方案

  • 移除processRowPostCreate中的自动调整逻辑,该钩子触发时新挂载的虚拟列单元格还未完成内容绘制,计算结果无参考价值
  • 所有列宽计算逻辑通过requestAnimationFrame推迟到浏览器下一帧渲染周期执行,确保DOM已经完成绘制,能拿到准确的内容宽度
  • 给滚动触发的列宽调整逻辑加防抖,快速滚动时只在滚动停止100~150ms后执行一次计算,避免不必要的重排消耗
  • 监听onFirstDataRendered事件,首屏数据渲染完成后主动触发一次列宽计算,解决初始化不生效的问题
  • 增加已计算列缓存,已经调整过宽度的列重复滚动到时不再重复计算,减少性能消耗

优化后参考代码

import { AgGridReact } from 'ag-grid-react';
import { GridOptions, VirtualColumnsChangedEvent } from 'ag-grid-community';
import { useMemo, useRef, useCallback } from 'react';

// 通用防抖工具
const debounce = <T extends (...args: any[]) => void>(fn: T, delay: number) => {
  let timer: NodeJS.Timeout | null = null;
  return (...args: Parameters<T>) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  }
}

const App = () => {
  const gridRef = useRef<AgGridReact>(null);
  // 缓存已完成宽度计算的列ID,避免重复计算
  const sizedColumnsCache = useRef<Set<string>>(new Set());

  // 核心列宽调整逻辑
  const autoSizeVisibleColumns = useCallback(() => {
    const columnApi = gridRef.current?.columnApi;
    if (!columnApi) return;

    // 等当前帧DOM渲染完成后再执行计算
    requestAnimationFrame(() => {
      const virtualColumns = columnApi.getAllDisplayedVirtualColumns();
      const pendingResizeCols = virtualColumns.filter(
        col => !sizedColumnsCache.current.has(col.getColId())
      );
      
      if (pendingResizeCols.length === 0) return;
      
      columnApi.autoSizeColumns(pendingResizeCols, false);
      pendingResizeCols.forEach(col => sizedColumnsCache.current.add(col.getColId()));
    })
  }, [])

  // 防抖包装滚动触发的调整逻辑,快速滚动时不重复触发
  const debouncedResize = useMemo(
    () => debounce(autoSizeVisibleColumns, 120),
    [autoSizeVisibleColumns]
  );

  const gridOptions = useMemo<GridOptions>(
    () => ({
      suppressColumnVirtualisation: false,
      suppressRowVirtualisation: false,
      // 首屏数据渲染完成后触发第一次列宽调整
      onFirstDataRendered: () => autoSizeVisibleColumns()
    }),
    [autoSizeVisibleColumns]
  );

  const data = useMemo<any[]>(() => {
    return createRandomGridData(100, 200, { resizable: true });
  }, []);

  const onVirtualColumnsChanged = useCallback(
    (_event: VirtualColumnsChangedEvent) => debouncedResize(),
    [debouncedResize]
  );

  return (
    <div className="ag-theme-alpine" style={{ height: '98vh', width: '98vw' }}>
      <AgGridReact
        ref={gridRef}
        gridOptions={gridOptions}
        rowData={data[0]}
        columnDefs={data[1]}
        enableRangeSelection={true}
        enableFillHandle={true}
        suppressClearOnFillReduction={true}
        onVirtualColumnsChanged={onVirtualColumnsChanged}
      />
    </div>
  );
};

补充说明

如果单元格使用了自定义渲染器、存在异步加载内容(比如图片、接口返回的文本),需要在对应内容加载完成后,手动调用autoSizeColumns针对对应列重新计算宽度,否则会出现宽度匹配不准的问题。防抖时长可以根据表格数据量灵活调整,数据量越大可以适当拉长防抖间隔,优先保证滚动流畅度。


内容的提问来源于stack exchange,提问作者mottosson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 14:21:20