# @ehfuse/mui-virtual-data-table 中文文档

规范地址：https://npmcn.com/package/%40ehfuse/mui-virtual-data-table/

版本：1\.11\.4
npm 元数据更新时间：2026-11-03T06:19:35.764Z
中文译文更新时间：2026-11-13T09:30:30.040Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40ehfuse/mui-virtual-data-table
原文对照：https://npmcn.com/package/%40ehfuse/mui-virtual-data-table/?lang=original
项目主页：https://github.com/ehfuse/mui-virtual-data-table#readme
源码仓库：https://github.com/ehfuse/mui-virtual-data-table

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：765 kB，14 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=16\.0\.0
- 运行依赖：2 个
- peer 依赖：react \>=16\.8\.0，react-dom \>=16\.8\.0，@mui/material \>=5\.0\.0，@emotion/react \>=11\.0\.0，react-virtuoso \>=4\.0\.0，@emotion/styled \>=11\.0\.0
- 首次发布：2025-10-01T10:01:54.476Z
- 最新版本发布：2026-11-03T06:19:35.521Z
- 近 30 天下载量：5770（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：78（近 12 个月 69 个）

## 中文简介

与 Materi-UI 集成反应的高性能虚拟数据表组件

## 安装

```sh
npm install @ehfuse/mui-virtual-data-table
```

也可以使用：`pnpm add @ehfuse/mui-virtual-data-table`、`yarn add @ehfuse/mui-virtual-data-table`、`bun add @ehfuse/mui-virtual-data-table`

## README

# MUI 虚拟数据表

一个高性能的虚拟数据表组件,用于与Material-UI集成进行React,以虚拟化渲染,无限卷轴,键盘导航,自定义型号为主.

(原始内容存档于2018-09-29). React QQ Material-UI QQ. 가상화,무한也.

## 特征

-   🚀 **高性能**: 虚拟渲染用反应-虚拟来平滑地用大数据集滚动
-   ♾️ **无限滚动**: 内置有自动加载指示器的无限滚动
-   ⌨️ **键盘导航**: 完整的键盘支持(箭头键,PageUp/Down,Home/End)
-   🎨 **物质-UI 整合**: 与MUI组件无缝集成
-   🦓 **斑马脱衣**: 带有自定义颜色支持的可选交替行颜色
-   🔧 **类型脚本支持**: 包含全面类型定义的完整类型脚本支持
-   🔄 **排序**: 内置列排序功能
-   🎯 **可自定义**: 高度自定义的列、样式和滚动条选项
-   📊 **空状态**: 内建空状态处理自定义消息
-   🔍 **装入状态**: 有淡出动画的平滑加载过渡
-   📏 **分组信头**: 支持带有动态高度计算的分组列标题
-   🎨 **自定义滚动栏**: 带有完全定制选项的美丽的自定义滚动条

## 주요

-   🚀 **고성능**{\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}你觉得呢
-   ♾️ **무한**자동:인디케이터가也
-   ⌨️ **키보드**:
-   🎨 **物质UI**翻译:
-   🦓 **얼룩말**{\\fn黑体\\fs20\\shad2\\2aH82\\3aH20\\4aH33\\fscx95\\3cH592001\\be1}你觉得怎样
-   🔧 **类型脚本QQ**:====================================================================================================== (======================================================================
-   🔄 **정렬**내장된:
-   🎯 **커스터마이징**컬럼:컬럼也
-   📊 **빈**커스텀:지원하는也
-   🔍 **로딩**{\\fn黑体\\fs22\\bord1\\shad0\\3aHBE\\4aH00\\fscx67\\fscy66\\2cHFFFFFF\\3cH808080}你觉得呢
-   📏 **그룹**동적:계산을也
-   🎨 **커스텀**완전한:옵션을也

## 安装

```bash
npm install @ehfuse/mui-virtual-data-table
```

## 同伴依赖关系

此软件包需要以下对等依赖性:

```bash
npm install react react-dom @mui/material react-virtuoso
```

## 页:1

详细的API参考和使用示例:

-   **[한국어](https://npmcn.com/package/%40ehfuse/mui-virtual-data-table/docs/ko/getting-started.md)** - API 설명 및 가이드
-   **[{\\fn黑体\\fs22\\bord1\\shad0\\3aHBE\\4aH00\\fscx67\\fscy66\\2cHFFFFFF\\3cH808080}你觉得呢](https://npmcn.com/package/%40ehfuse/mui-virtual-data-table/docs/ko/example.md)** - 다양한 사용
-   **[开始(英文)](https://npmcn.com/package/%40ehfuse/mui-virtual-data-table/docs/en/getting-started.md)** - API 参考和指南 _(快来了)_

## 快速启动

```tsx
import { VirtualDataTable } from "@ehfuse/mui-virtual-data-table";
import type { DataColumn } from "@ehfuse/mui-virtual-data-table";

interface User {
    id: number;
    name: string;
    email: string;
}

const columns: DataColumn<User>[] = [
    { id: "id", text: "ID", width: 80 },
    { id: "name", text: "Name", width: 200, sortable: true },
    { id: "email", text: "Email", width: 250 },
];

const data: User[] = [
    { id: 1, name: "John Doe", email: "john@example.com" },
    { id: 2, name: "Jane Smith", email: "jane@example.com" },
    // ... more data
];

function App() {
    return (
        <VirtualDataTable
            data={data}
            columns={columns}
            totalCount={data.length}
            loading={false}
        />
    );
}
```

## 基本支持签名

```tsx
<VirtualDataTable
    // Required
    data={T[]}                              // Data array
    columns={DataColumn<T>[]}               // Column definitions
    totalCount={number}                     // Total data count

    // Optional - Styling
    striped={boolean | string}              // Zebra striping (true: default gray (#f5f5f5), string: custom color, default: false)
    rowDivider={boolean}                    // Show row borders (default: true)
    rowHeight={number}                      // Fixed row height in px (default: 50) — rows keep this height even when showFooter fills a taller container
    columnHeight={number}                   // Header height in px (default: 56, auto 2x for grouped headers)
    showPaper={boolean}                     // Wrap in Paper component (default: true)
    paddingX={string | number}              // Horizontal padding (default: "1rem")
    paddingTop={string | number}            // Top padding (default: 0)
    paddingBottom={string | number}         // Bottom padding (default: 0)
    variant={"default" | "notion"}          // Table look. "notion": Notion-like (40px header, 13px grey header text, cell dividers, flat hover) — matches @ehfuse/taskbox tables
    rowHoverColor={string}                  // Row hover background color (default: "#000000", auto-inverted brightness in dark mode)
    rowHoverOpacity={number}                // Row hover opacity 0-1 (default: 0.06)
    viewportBuffer={number | { top, bottom }} // Viewport outside pre-render buffer in px (default: rowHeight-based auto buffer)

    // Optional - Infinite Scroll
    loading={boolean}                       // Loading state (default: false)
    onLoadMore={(offset, limit) => void}    // Load more callback (enables infinite scroll when provided)
    // ⚠️ onLoadMore 계약: 호출되면 반드시 이행해서 data 가 늘어나거나 loading 이 토글돼야 합니다.
    //    (정렬/검색/필터로 목록을 1페이지로 되돌리면 컴포넌트가 자동으로 더보기 잠금을 풀어 재개합니다.)

    // Optional - Sorting
    sortBy={string}                         // Current sort field
    sortDirection={"asc" | "desc"}          // Current sort direction
    onSort={(id, direction) => void}        // Sort callback

    // Optional - Interactions
    onRowClick={(item, index) => void}      // Row click handler

    // Optional - Customization
    emptyMessage={string | React.ReactNode} // Empty state message (default: "NO DATA")
    scrollbars={VDTOverlayScrollbarProps}   // Custom scrollbar options
    LoadingComponent={React.ComponentType}  // Custom loading component

    // Optional - Layout (1.2.0)
    framed={boolean}                        // notion: outer border + paper padding (default: true). false = fill an already-bordered box
    headerTopBorder={boolean}               // notion + framed=false: draw a top line on the header row (table shares a box with a toolbar above)
    tableLayout={"fixed" | "auto"}          // "auto": column widths follow content, wide tables scroll horizontally
    rowHeightMode={"fixed" | "auto"}        // "auto": rowHeight becomes a minimum, rows grow with content
    autoHeight={boolean}                    // No virtualization / no own scroll — the table is as tall as its rows (page scrolls)
    renderFullWidthRow={(item, index) => ReactNode | null} // Group band row: one cell spanning all columns (null = normal row)
    fullWidthRowSx={SxProps}                // Style of the band row cell (background, padding)
    onRowContextMenu={(item, index, event) => void}

    // Optional - 1.3.0
    pinnedRows={T[]}                        // Rows pinned under the header (always visible, same columns/cells/band rows)
    pinnedRowsMaxHeight={number}            // Max height of the pinned area in px — it scrolls on its own beyond that
    pinnedRowsSx={SxProps}                  // Pinned area style (default: 1px strong bottom border)
    resetScrollOnDataChange={boolean}       // false: keep the scroll position when rows are removed (collapse groups); remount with key to reset
    rowKeyFromId={boolean}                  // Use getRowId as the React key of rows (rows with inline editors)
    onEndReached={() => void}               // Fired every time the end is reached (no minimum / no same-length latch)
    colorMode={"light" | "dark"}            // notion colors independent of the MUI palette mode
    borderColor={string}                    // notion: color for every line (outer frame, header line, cell dividers). Default NOTION_LINE_COLOR (#d9d8d5, exported) — every notion line uses one color
    mobile={boolean | { query?, renderRow? }} // 1.6.0 card list on narrow screens (see "Mobile list")
    scrollParent={HTMLElement | null}         // 1.7.0 virtualize against an outer (page) scroller
    columnResize={boolean | { minWidth? }}    // 1.8.0 drag the header's right edge to resize a column (see "Column resize")
    columnWidths={Record<string, number>}     // 1.8.0 resized widths by column id (you store them)
    onColumnWidthsChange={(widths) => void}   // 1.8.0 fired once on release / on double-click reset
    rowReorder={{ onReorder, canDrag?, disabledReason?, handleWidth?, handleLabel? }} // 1.9.0 drag rows by a handle (see "Row reorder")

    // Optional - 1.4.0 header context menu (right-click on th never shows the browser menu)
    onSortClear={() => void}                // adds "정렬 풀기" to the header menu (and re-picking the active direction clears)
    onHeaderContextMenu={(column, event) => boolean | void} // custom header menu — return true or preventDefault() to skip the built-in one
/>
```

### 列选项 (1.2.0)

```tsx
{ id: "name", text: "이름", width: 160, pinned: "left" }     // sticky left (numeric width needed for the next pinned column)
{ id: "name", text: "이름", primary: true }                  // 15px body text for the row's main cell
{ id: "d1", text: "월", onCellClick: (item, i, e) => {} }    // per-cell click + cell hover (row click is not fired)
{ id: "who", text: "담당", rowSpan: (item, i) => 2 }         // merge down; return 0 on covered rows (use with autoHeight / short tables)
{ id: "m1", text: "1월", cellPadding: "4px 0" }              // body padding override
{ id: "t", text: "업무", cellStyle: (item) => ({ color: "#999" }) } // per-row cell style
{ id: "c", text: "열", headerProps: { draggable: true, onDrop } }  // extra th attributes (drag to reorder, title…)
{ id: "who", text: "책임자", width: 110, colSpan: (item) => (item.isEvent ? 2 : 1) } // 1.3.0: merge right (the covered column is not drawn)
{ id: "title", text: "제목", minWidth: 220 }                // 1.3.0: flexible column (no width) with a minimum — table min width = sum, scrolls sideways
```

页眉上下文菜单(1.4.0)-右键单击页眉单元格:

```tsx
// sort items appear for sortable columns when onSort is given
{ id: "name", text: "이름", sortable: true }
// filter checklist (multi by default; filterMultiple: false = pick one)
{
    id: "status", text: "상태",
    filterOptions: [{ value: "open", label: "진행" }, { value: "done", label: "완료" }],
    filterValues: status,                 // string[] — non-empty → header turns primary + filter icon
    onFilterChange: (values) => setStatus(values),
}
{ id: "memo", text: "메모", headerMenu: false } // no menu on this column (browser menu still blocked)
{ id: "due", text: <MyHeader />, sortMenu: true } // 1.4.1: sort items in the menu without VDT's sort label/click (custom-drawn header)

// open your own header menu instead
<VirtualDataTable onHeaderContextMenu={(col, e) => (col.id === "owner" ? (openOwnerMenu(e), true) : false)} … />
```

共享页眉菜单( 1. 5.0) – 从自定义页眉按钮/ 工具栏按钮中自己打开 :

```tsx
import { HeaderMenu, headerMenuAnchor } from "@ehfuse/mui-virtual-data-table";

const [pos, setPos] = useState<{ left: number; top: number } | null>(null);
<button onClick={(e) => setPos(headerMenuAnchor(e.currentTarget))}>상태</button>
<HeaderMenu
    open={Boolean(pos)}
    anchorPosition={pos}
    onClose={() => setPos(null)}
    sort={{ active: sortDir, onChange: (d) => setSortDir(d) }}           // null = 풀기
    filter={{
        options: [{ value: "open", label: "진행", icon: <Dot color="green" /> }],
        value: picked, onChange: setPicked,
        multiple: true,                 // false = radio (closes on pick), allLabel: "전체"
        emptyLabel: "비어 있음",        // adds value ""
        searchable: undefined,          // default: > 10 options
    }}
>
    {/* extra items */}
</HeaderMenu>
```

平接行实例 (1.3. 0) :

```tsx
<VirtualDataTable
    variant="notion"
    data={rows}                                   // without the pinned ones
    pinnedRows={[{ kind: "band", label: "미확인" }, ...unconfirmed]}
    pinnedRowsMaxHeight={49 * 6}                  // band + 5 rows, then it scrolls
    renderFullWidthRow={(r) => (r.kind === "band" ? r.label : null)}
    getRowId={(r) => r.id}
    rowKeyFromId
    resetScrollOnDataChange={false}
    ...
/>
```

概念默认 (1. 2. 0): 第48行, 标题文本 13px, 所选行(`selectedRowId`\= (单位:千美元) `#dbeafe` + 3px 键 `#3b82f6` 左栏(包含暗值), 字符串 `emptyMessage` =头下为一浅灰色线条,页脚细胞使用体垫.

### 页面滚动 (1.7. 0)

`scrollParent={element}` 虚拟化对照外向滚动器(反应-虚拟) `customScrollParent`)——该表本身没有卷轴 并随页面而增长,所以捆绑到页面卷轴上的东西(collapsing app bar,滚动到上,拉到再刷)继续工作. 过 `null` ,而该元素尚不为人所知(在此之前没有绘图)。 与 `mobile` 财务报告和审定财务报表 `onEndReached`。 。 。 .

### 列大小调整(1.8.0)

`columnResize` 抓住每个信头的右边缘(`col-resize` 光标,悬浮上的细线). 拖动它来调整列的大小, 双击它,然后回到专栏 `width`。表格不存储任何东西——将宽度保留在您喜欢的地方 (账户设置, 本地Storage...) 并用 `columnWidths`没有 `columnWidths` 桌子只记得他们 直到它重新挂起.

```tsx
const [widths, setWidths] = useAccountSetting("customer.list"); // Record<columnId, px>
<VirtualDataTable
    columnResize                              // or { minWidth: 60 } — default minimum 48px (a column's own minWidth wins)
    columnWidths={isPhone ? undefined : widths}
    onColumnWidthsChange={setWidths}          // once on release; double-click → the key is removed
    ...
/>
```

-   虽然只将 CSS 变量拖入到表格框变化中——行不重递,但虚拟化和水平卷轴保持不变.
-   拖曳之后的点击被吞噬, 因此释放到一个标题上 永远不要排序 / 打开标题菜单 .
-   灵活列( 无) `width`(b) 保持最低限度的 `minWidth` (或调整最小大小) 一旦任何东西被调整大小; 当每列结束 表格具有像素宽度,宽度与总和一样(没有拉伸使所拉宽与所拉宽不同). 如果没有一列是灵活的,第一个拖放会捕捉到每个一列所绘制的宽度(1.8.1),所以其他一列不会跳出.
-   与 `pinned: "left"` (粘贴相冲后), `autoHeight`, (中文(简体) ), `framed`, (中文(简体) ), `tableLayout`, (中文(简体) ), `variant="notion"`, (中文(简体) ), `pinnedRows`脚印. 在那个 `mobile` 没有卡片列表 `columnWidths` 被忽略.

### 行重排( 1. 9. 0)

`rowReorder` 添加拖动手柄( QQ) 为第一个单元格 。 拖放以移动行; 显示行将降落在哪里 `onReorder(from, to, item)` 释放时起火(`to` 是行尾的索引——与 `arrayMove(data, from, to)`) (中文(简体) ). 桌子永远不变 `data`· ; 自己重新订购,传回.

```tsx
<VirtualDataTable
    rowReorder={{
        onReorder: (from, to) => setRows((r) => arrayMove(r, from, to)),
        disabledReason: sortBy ? "정렬을 풀면 옮길 수 있어요" : undefined, // handle is dimmed, drag is off
        canDrag: (row) => !row.locked,                                    // no handle on locked rows
    }}
    ...
/>
```

-   拖曳时没有再退让——被拖走的行得到一个 `data-vdt-dragging` 属性( dimmed) 和放行是一个位置元素, 因此它保持轻与虚拟化和长列表。 拖入边缘自动滚动(表卷轴, `scrollParent`,或文档).
-   键盘:将手柄聚焦于 → 空间(升起) → / (移动一步, `onReorder` 空间 / 输入 / Esc( 投放) .
-   在那个 `mobile` 卡片列表 手柄坐落在卡片左边. 与 `pinned: "left"` 手柄柱也被固定. 输入行 `pinnedRows` 无法移动 。 与 `autoHeight`, (中文(简体) ), `scrollParent`, (中文(简体) ), `variant="notion"`, (中文(简体) ), `columnResize`。 。 。 .
-   除手柄外,行点击仍然在任何地方工作;拖动后点击不作为行点击处理.

### 移动列表 (1.6.0)

`mobile` 将表格转换为窄屏幕上的卡片列表(默认查询) `(max-width:599.95px)`:没有标题行,每行一张牌, 虚拟化/ `onLoadMore` 编号 : `onRowClick` 编号 : `selectedRowId` 一样的工作。 上面的菜单、页脚和被绑的行不画在那里.

```tsx
<VirtualDataTable
    mobile                                   // or { query: "(max-width:899.95px)", renderRow: (item, index) => <MyCard/> }
    columns={[
        { id: "thumb", text: "", mobile: "thumb", render: (r) => <img src={r.image} width={56} /> },
        { id: "title", text: "작품", mobile: "title" },
        { id: "status", text: "상태", mobile: "badge", render: (r) => <Chip label={r.status} /> },
        { id: "artist", text: "작가", mobile: "sub" },
        { id: "price", text: "판매가" },              // no hint → meta line "판매가 1,000,000원"
        { id: "memo", text: "메모", mobile: false },  // hidden on mobile
    ]}
    ...
/>
```

槽数 : `title` (粗体一行-默认:第一栏), `thumb` (左边), `badge` (标题权), `sub` (标题下行), `meta` ("头值"对相接-其他列的默认), `false` (隐也. `mobileLabel` 覆盖元标签( n)`false` = 仅值).

## 许可证

麦特 金·杨金[(原始内容存档于2018-10-21). ehfuse@gmail.com](mailto:ehfuse@gmail.com)(中文(简体) )

## 捐款

欢迎捐款! 请随意提交拉力请求.

## 支助

支持, 电子邮件 [(原始内容存档于2018-10-21). ehfuse@gmail.com](mailto:ehfuse@gmail.com) 或者在 GitHub 上创建一个问题.

## 常见问题

### @ehfuse/mui-virtual-data-table 是什么？

与 Materi-UI 集成反应的高性能虚拟数据表组件

### 如何安装 @ehfuse/mui-virtual-data-table？

在项目目录运行 npm install @ehfuse/mui-virtual-data-table，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：react（\>=16\.8\.0）、react-dom（\>=16\.8\.0）、@mui/material（\>=5\.0\.0）、@emotion/react（\>=11\.0\.0）、react-virtuoso（\>=4\.0\.0）、@emotion/styled（\>=11\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @ehfuse/mui-virtual-data-table 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @ehfuse/mui-virtual-data-table 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @ehfuse/mui-virtual-data-table 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=16\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @ehfuse/mui-virtual-data-table 可以商用吗？

@ehfuse/mui-virtual-data-table 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @ehfuse/mui-virtual-data-table 的下载量有多少？

根据 npm 官方下载统计，@ehfuse/mui-virtual-data-table 在 2026/10/13 – 2026/11/11 这 30 天内被下载 5,770 次，日均约 192 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @ehfuse/mui-virtual-data-table 还在维护吗？

最新版本 1\.11\.4 发布于 2026/11/03（10 天前）；至今共发布 78 个版本，其中过去 12 个月发布了 69 个；首次发布于 2025/10/01。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

