npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aimerthyr/virtual-table

v1.4.0

Published

高性能虚拟滚动表格

Readme

@aimerthyr/virtual-table

English | 简体中文

基于 Vue 3、TanStack Table 和 TanStack Virtual 的高性能虚拟滚动表格组件。

介绍

@aimerthyr/virtual-table 是一个面向中大数据量场景的 Vue 3 表格组件,支持虚拟滚动、排序、筛选、分页、树形数据、展开行、行选择、固定表头、固定列、列宽调整等常用能力,并提供完整的 TypeScript 类型支持与插槽扩展能力。

特性

  • 基于虚拟滚动实现大数据量高性能渲染
  • 支持排序、筛选、分页、树形数据、展开行、行选择
  • 支持固定表头、固定列、列宽调整
  • 支持自定义单元格、自定义筛选、自定义分页、自定义主题
  • 基于 Vue 3 Composition API 与 TypeScript 构建
  • 底层依赖 TanStack Table 与 TanStack Virtual,扩展能力强

官网

在线地址: https://aimerthyr.github.io/virtual-table/

安装

# pnpm
pnpm add @aimerthyr/virtual-table

# npm
npm install @aimerthyr/virtual-table

# yarn
yarn add @aimerthyr/virtual-table

依赖要求

  • vue >= 3.5.0

快速开始

// main.ts 中导入
import '@aimerthyr/virtual-table/virtual-table.css'
<script setup lang="ts">
import { ref } from 'vue'
import VTable, { type VTableColumn } from '@aimerthyr/virtual-table'
import '@aimerthyr/virtual-table/virtual-table.css'

const data = ref([
  { id: 1, name: '张三', age: 28, address: '北京市朝阳区' },
  { id: 2, name: '李四', age: 32, address: '上海市浦东新区' },
  { id: 3, name: '王五', age: 25, address: '广州市天河区' },
])

const columns: VTableColumn[] = [
  { columnKey: 'id', columnHeader: 'ID', columnWidth: 80 },
  { columnKey: 'name', columnHeader: '姓名', columnWidth: 120 },
  { columnKey: 'age', columnHeader: '年龄', columnWidth: 100, columnAlign: 'center' },
  { columnKey: 'address', columnHeader: '地址' },
]
</script>

<template>
  <!-- 建议设置 row-height,以获得更稳定的虚拟滚动体验 -->
  <VTable :data="data" :columns="columns" :row-height="44" bordered />
</template>

导入方式

import VTable from '@aimerthyr/virtual-table'

或:

import { VTable } from '@aimerthyr/virtual-table'

同时也支持按需导入类型:

import type {
  VTableChangeState,
  VTableColumn,
  VTableColumnSizingState,
  VTableData,
  VTableExpandedState,
  VTableInstance,
  VTablePaginationState,
  VTableProps,
} from '@aimerthyr/virtual-table'

常见用法

1. 启用排序和筛选

<script setup lang="ts">
import VTable, { type VTableColumn } from '@aimerthyr/virtual-table'
import '@aimerthyr/virtual-table/virtual-table.css'

const data = [
  { id: 1, name: '张三', age: 28 },
  { id: 2, name: '李四', age: 32 },
]

const columns: VTableColumn[] = [
  {
    columnKey: 'name',
    columnHeader: '姓名',
    columnWidth: 150,
    columnEnableSort: true,
    columnEnableFilter: true,
  },
  {
    columnKey: 'age',
    columnHeader: '年龄',
    columnWidth: 100,
    columnEnableSort: true,
  },
]
</script>

<template>
  <VTable :columns="columns" :data="data" />
</template>

2. 自定义单元格

<template>
  <VTable :columns="columns" :data="data">
    <template #bodyCell="{ columnKey, row }">
      <strong v-if="columnKey === 'name'">{{ row.name }}</strong>
      <template v-else>{{ row[columnKey] }}</template>
    </template>
  </VTable>
</template>

3. 分页

<script setup lang="ts">
import { ref } from 'vue'
import VTable, {
  type VTableChangeState,
  type VTableColumn,
  type VTablePaginationState,
} from '@aimerthyr/virtual-table'

const loading = ref(false)
const totalCount = ref(200)

const pagination = ref<VTablePaginationState>({
  pageIndex: 1,
  pageSize: 10,
})

const tableData = ref<any[]>([])

const columns: VTableColumn[] = [
  { columnKey: 'id', columnHeader: 'ID', columnWidth: 80 },
  { columnKey: 'name', columnHeader: '姓名', columnWidth: 120 },
  { columnKey: 'age', columnHeader: '年龄', columnWidth: 100 },
]

const fetchData = async (page: number, pageSize: number) => {
  loading.value = true

  // 示例:实际项目中这里替换为你的接口请求
  await new Promise((resolve) => setTimeout(resolve, 300))

  const start = (page - 1) * pageSize
  tableData.value = Array.from({ length: pageSize }, (_, i) => ({
    id: start + i + 1,
    name: `用户${start + i + 1}`,
    age: 20 + ((start + i) % 20),
  }))

  loading.value = false
}

const handleTableChange = (state: VTableChangeState) => {
  const { pageIndex, pageSize } = state.pagination
  pagination.value = { pageIndex, pageSize }
  fetchData(pageIndex, pageSize)
}

fetchData(pagination.value.pageIndex, pagination.value.pageSize)
</script>

<template>
  <VTable
    v-model:default-pagination="pagination"
    :data="tableData"
    :columns="columns"
    :loading="loading"
    :pagination-config="{
      enabled: true,
      total: totalCount,
      placement: 'right',
    }"
    @table-change="handleTableChange"
  />
</template>

4. 列宽调整

<script setup lang="ts">
import { ref } from 'vue'
import VTable, { type VTableColumn, type VTableColumnSizingState } from '@aimerthyr/virtual-table'

const columnSizing = ref<VTableColumnSizingState>({})

const data = [
  { id: 1, name: '张三', age: 28, email: '[email protected]' },
  { id: 2, name: '李四', age: 32, email: '[email protected]' },
]

const columns: VTableColumn[] = [
  { columnKey: 'id', columnHeader: 'ID', columnWidth: 80, columnEnableResize: true },
  { columnKey: 'name', columnHeader: '姓名', columnWidth: 120, columnEnableResize: true },
  { columnKey: 'age', columnHeader: '年龄', columnWidth: 100, columnEnableResize: true },
  { columnKey: 'email', columnHeader: '邮箱', columnEnableResize: true },
]

const handleColumnSizingChange = (state: VTableColumnSizingState) => {
  console.log('当前列宽状态:', state)
}
</script>

<template>
  <VTable
    v-model:default-column-sizing="columnSizing"
    :data="data"
    :columns="columns"
    column-resize-mode="onEnd"
    @column-sizing-change="handleColumnSizingChange"
  />
</template>

5. 行选择

<script setup lang="ts">
import VTable, { type VTableColumn } from '@aimerthyr/virtual-table'

const data = [
  { id: 1, name: '张三', age: 28 },
  { id: 2, name: '李四', age: 32 },
  { id: 3, name: '王五', age: 25 },
]

const columns: VTableColumn[] = [
  { columnKey: 'name', columnHeader: '姓名' },
  { columnKey: 'age', columnHeader: '年龄' },
]

const handleSelectionChange = (rows: any[]) => {
  console.log('选中的行:', rows)
}
</script>

<template>
  <VTable
    :data="data"
    :columns="columns"
    :row-selection-config="{
      enabled: true,
      onChange: handleSelectionChange,
    }"
  />
</template>

Props

在 Vue 模板中请使用 kebab-case,例如 rowHeight 对应 row-height

| 属性名 | 类型 | 默认值 | 说明 | | ---------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------- | | data | TData[] | [] | 表格数据源 | | columns | VTableColumn[] | [] | 列配置 | | rowHeight | number | undefined | 行高,建议设置以优化虚拟滚动表现 | | rowKey | string \| number \| ((row) => string \| number) | undefined | 行唯一标识 | | loading | boolean | false | 是否加载中 | | fixedHeader | boolean | true | 是否固定表头 | | enableSortingRemoval | boolean | true | 是否允许取消排序 | | bordered | boolean | false | 是否显示边框 | | rowSelectionConfig | VTableRowSelectionConfig<TData> | { enabled: false } | 行选择配置 | | loadMoreConfig | VTableLoadMoreConfig | { showNoMore: false, noMoreText: '没有更多了' } | 滚动加载更多配置 | | paginationConfig | VTablePaginationConfig | { enabled: false, placement: 'right', total: 0, mode: 'server' } | 分页配置 | | treeConfig | VTableTreeConfig | { enabled: false, childrenKey: 'children', indentSize: 16 } | 树形结构配置 | | enableExpandRow | boolean | false | 是否启用展开行 | | enableRowHover | boolean | true | 是否启用行 hover 高亮 | | adaptiveColumnWidth | number | 120 | 自适应列最小宽度 | | defaultExpandAllRows | boolean | false | 是否默认展开所有行,仅初始化生效 | | columnResizeMode | 'onChange' \| 'onEnd' | 'onChange' | 列宽调整模式 | | fixedFooter | boolean | false | 是否固定表尾 | | themeConfig | VTableThemeConfig | {} | 自定义主题配置 | | defaultCheckboxColumnWidth | number | 40 | 默认 checkbox 列宽 | | defaultExpandColumnWidth | number | 42 | 默认展开列宽 | | customRowAttributes | (row, rowIndex) => HTMLAttributes | () => ({}) | 自定义行属性 | | customHeaderCellAttributes | (column, colIndex) => ThHTMLAttributes | () => ({}) | 自定义表头单元格属性 | | customCellAttributes | (row, column, rowIndex, colIndex) => TdHTMLAttributes \| null | () => ({}) | 自定义表体单元格属性,返回 colspanrowspan0 时不渲染 |

列配置 VTableColumn

| 属性名 | 类型 | 说明 | | -------------------- | ------------------------------- | -------------------------- | | columnKey | string | 列唯一 key,同时对应字段名 | | columnHeader | string \| VNode \| Function | 列头内容 | | columnAlign | 'left' \| 'center' \| 'right' | 列对齐方式 | | columnWidth | number \| string | 列宽,支持数字或百分比 | | columnEnableSort | boolean | 是否启用排序 | | columnEnableFilter | boolean | 是否启用筛选 | | columnCell | Function | 单元格渲染函数 | | columnEnableResize | boolean | 是否允许调整列宽 | | columnMaxWidth | number | 列最大宽度 | | columnMinWidth | number | 列最小宽度,默认 50 | | columnChildren | VTableColumn[] | 子列,用于表头分组 |

Events

在 Vue 模板中,事件请使用 kebab-case 形式监听。

| 事件名 | 回调参数 | 说明 | | ---------------------- | ------------------------------------------ | ---------------------------------------- | | table-change | (state: VTableChangeState) => void | 表格状态变化时触发,包括分页、排序、筛选 | | scroll-to-bottom | () => void | 滚动到底部时触发 | | expanded-rows-change | (state: VTableExpandedState) => void | 展开行变化时触发 | | column-sizing-change | (state: VTableColumnSizingState) => void | 列宽调整时触发 |

Slots

| 插槽名 | 参数 | 说明 | | ---------------------- | -------------------------------------------------------------- | ---------------------- | | customHeader | { columns, table } | 自定义整个表头 | | bodyCell | { columnKey, column, row, rowIndex } | 自定义单元格内容 | | headerCell | { columnKey, column } | 自定义表头单元格 | | customFilterIcon | { columnKey, filtered, column } | 自定义筛选图标 | | customFilterDropdown | { confirm, reset, setFilterValue, column, filterModelValue } | 自定义筛选下拉内容 | | expandedRowRender | { row } | 自定义展开行内容 | | customPopover | { open, onOpenChange, trigger, content } | 自定义 Popover | | customPagination | { pageSize, pageIndex, total, onPageChange } | 自定义分页器 | | customCheckbox | { checked, disabled, indeterminate, onCheckedChange } | 自定义复选框 | | customEmpty | - | 自定义空状态 | | customLoadingIcon | - | 自定义 loading 图标 | | customLoadNoMore | - | 自定义“没有更多了”区域 | | customFooter | - | 自定义表尾 | | customExpandIcon | { expand, onExpandChange } | 自定义展开图标 |

实例方法

组件暴露了以下实例能力:

| 方法名 | 类型 | 说明 | | --------------- | -------------------------------------------------------- | ------------------- | | tanstackTable | Table<TData> | TanStack Table 实例 | | scrollToIndex | (index: number, behavior?: 'auto' \| 'smooth') => void | 滚动到指定行 | | setEditingRow | (rowId: string \| number \| null) => void | 设置当前编辑行 |

使用示例:

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import VTable, { type VTableInstance } from '@aimerthyr/virtual-table'

const tableRef = ref<VTableInstance | null>(null)

onMounted(() => {
  tableRef.value?.scrollToIndex(20, 'smooth')
})
</script>

<template>
  <VTable ref="tableRef" :data="data" :columns="columns" />
</template>

主题配置

themeConfig 支持覆盖默认主题配置,主要结构如下:

type VTableThemeConfig = {
  primaryColor?: string
  header?: {
    color?: string
    backgroundColor?: string
    borderRadius?: number
    splitColor?: string
    headerIconColor?: string
    padding?: number
  }
  body?: {
    color?: string
    backgroundColor?: string
    padding?: number
  }
  border?: {
    borderStyle?: 'solid' | 'dashed'
    borderColor?: string
  }
  rowHoverColor?: string
  zIndex?: {
    pinnedColumn?: number
    fixedHeader?: number
    fixedFooter?: number
  }
}

示例:

<template>
  <VTable
    :data="data"
    :columns="columns"
    :theme-config="{
      primaryColor: '#1677ff',
      header: {
        backgroundColor: '#f5f7fa',
      },
      body: {
        backgroundColor: '#ffffff',
      },
      rowHoverColor: '#f5f7fa',
    }"
  />
</template>

使用建议

  • 大数据量场景下建议始终设置 row-height
  • 如果需要滚动加载,建议配合 loadingscroll-to-bottom 一起使用,避免重复触发
  • 分页模式下,如果是服务端分页,建议在 table-change 中统一处理分页、排序、筛选参数
  • 如果有树形数据,优先配置 tree-config
  • 如果需要自定义展开内容,使用 enable-expand-row 配合 expandedRowRender
  • 如果列很多并出现横向滚动,建议配置 adaptive-column-width

适用场景

  • 后台管理系统数据表格
  • 大数据量列表渲染
  • 需要虚拟滚动优化的业务表格
  • 需要可扩展插槽能力的 Vue 3 表格场景

开发

# 安装依赖
pnpm install

# 开发构建
pnpm dev

# 生产构建
pnpm build

相关链接

License

MIT