Олег Марков
Работа с таблицами во Vue через TanStack
Введение
Работа с таблицами – одна из частых задач при разработке интерфейсов во Vue. Обычно нужно не просто отрисовать данные в таблице, а добавить сортировку, фильтрацию, пагинацию, группировку, работу с большими массивами данных и гибкую настройку интерфейса. TanStack Table – современная библиотека для управления таблицами, которая дает все эти возможности. Причем, она работает как headless (без готового UI), что идеально подходит для проектов на Vue, где вы управляете разметкой полностью самостоятельно.
Здесь вы научитесь подключать и использовать TanStack Table во Vue, создавать кастомные таблицы, добавлять сортировку, фильтрацию и другие важные возможности. Я покажу практические примеры и объясню, как все устроено под капотом, чтобы вы могли делать свои интерфейсы легкими, производительными и удобными в поддержке.
Установка TanStack Table и интеграция с Vue
Начнем с базовой установки и создания первого примера.
Как установить TanStack Table
Вот как добавляется TanStack Table в проект на Vue 3 (TypeScript поддерживается по умолчанию):
npm install @tanstack/vue-table
Если вы работаете с Vue 2, используйте отдельную версию или следуйте документации TanStack Table для Legacy проектов.
Создание базовой таблицы
Давайте начнем с простого примера: таблица, которая просто отображает список объектов.
1. Подготовка данных
Сначала определим наши данные и колонки. Обычно это массивы объектов:
// Пример данных
const data = [
{ id: 1, name: 'Иван', age: 25 },
{ id: 2, name: 'Ольга', age: 34 },
{ id: 3, name: 'Петр', age: 28 },
]
Колонки задаются объектами с описанием, что мы хотим показывать и как:
const columns = [
{
header: 'Имя',
accessorKey: 'name', // Связываем с ключом объекта
},
{
header: 'Возраст',
accessorKey: 'age',
},
]
2. Объявление таблицы через TanStack
Теперь подключим таблицу с помощью фабрики:
import { useVueTable, createColumnHelper } from '@tanstack/vue-table'
// Здесь я использую useVueTable — основной хук для создания таблицы
const table = useVueTable({
data,
columns,
})
Это создает объект table, который содержит все необходимые методы и свойства для работы с таблицей.
3. Вывод таблицы в шаблоне
А теперь разметка для вывода таблицы (в SFC .vue файле):
<template>
<table>
<thead>
<tr>
<th v-for="header in table.getHeaderGroups()[0].headers" :key="header.id">
{{ header.column.columnDef.header }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in table.getRowModel().rows" :key="row.id">
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
{{ cell.getValue() }}
</td>
</tr>
</tbody>
</table>
</template>
Что здесь важно:
getHeaderGroups()
– для получения групп хедеров (можно использовать для сложных заголовков).getRowModel().rows
– предоставляет строки таблицы.row.getVisibleCells()
– доступ к ячейкам строки, которые вы выбрали для отображения.
Кастомизация колонок и ячеек
TanStack Table очень гибкая. Вы можете не просто выводить значения, а полностью кастомизировать вывод через render-функции.
Как сделать "смарт"-ячейки
Посмотрите пример – будет добавлен столбец "Действия" со своей кнопкой:
const columns = [
{
header: 'Имя',
accessorKey: 'name',
},
{
header: 'Возраст',
accessorKey: 'age',
},
{
header: 'Действия',
// Кастомный рендерer для ячейки
cell: ({ row }) => {
return `<button @click="alert('ID: ' + ${row.original.id})">Показать ID</button>`
}
}
]
Но для Vue мы не можем напрямую класть шаблон в строку. Решение — использовать scoped slots или возвращать функцию, а внутри компонента решать, что рендерить.
В типовом варианте TanStack Table во Vue предлагает использовать слоты для сложных столбцов.
Сортировка данных в таблице
Чтобы пользователи могли сортировать данные, достаточно добавить соответствующий плагин при инициализации таблицы и нажимать по заголовку столбца.
Подключение сортировки
- Подключаем модуль сортировки:
import { useVueTable, getSortedRowModel, getCoreRowModel } from '@tanstack/vue-table'
const table = useVueTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(), // Включаем сортировку
})
- Добавляем обработчик сортировки по клику:
<th
v-for="header in table.getHeaderGroups()[0].headers"
:key="header.id"
@click="header.column.toggleSorting()"
>
{{ header.column.columnDef.header }}
<!-- Показываем иконку направления сортировки -->
<span v-if="header.column.getIsSorted()">
{{ header.column.getIsSorted() === 'asc' ? '▲' : '▼' }}
</span>
</th>
Комментирую: при каждом клике таблица будет менять порядок сортировки по выбранному столбцу.
Фильтрация данных
Фильтрация — еще один стандартный кейс работы с таблицами. TanStack Table поддерживает быстрые фильтры и сложные функции.
Как добавить фильтрацию
- Импортируем фильтрационную функцию:
import { getFilteredRowModel } from '@tanstack/vue-table'
- Инициализируем таблицу с фильтрацией:
const table = useVueTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
})
- Создаем bind для значений фильтра:
<input
v-model="globalFilter"
placeholder="Фильтр по всем столбцам"
/>
- Передаем значение фильтра в таблицу:
const globalFilter = ref('')
watch(globalFilter, (value) => {
table.setGlobalFilter(value)
})
Поясняю: теперь при наборе текста будут обновляться данные в таблице.
Фильтрация по отдельному столбцу
Многие хотят фильтровать столбцы выборочно. TanStack Table поддерживает фильтры на уровне столбцов — просто добавьте фильтруемую функцию в определение столбца или используйте стандартные фильтры (например, "contains", "startsWith" и так далее).
Пагинация (Постраничная навигация)
Когда у вас большие объемы данных, то без пагинации не обойтись. Хорошая новость — TanStack Table дает полный контроль.
Пример добавления пагинации
- Импортируем и используем пагинационный плагин:
import { getPaginationRowModel } from '@tanstack/vue-table'
const table = useVueTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
})
- Добавляем кнопки для перехода между страницами:
<button @click="table.previousPage()" :disabled="!table.getCanPreviousPage()">Назад</button>
<button @click="table.nextPage()" :disabled="!table.getCanNextPage()">Вперед</button>
<span>
Страница {{ table.getState().pagination.pageIndex + 1 }} из {{ table.getPageCount() }}
</span>
Комментарий: методы previousPage() и nextPage() управляют страницами моделей таблицы.
Группировка данных и поддерево (SubRows)
Иногда нужно выводить иерархические, вложенные данные. TanStack Table отлично поддерживает группировку и вложенность строк.
Как добавить вложенные строки
Давайте рассмотрим ситуацию, где у каждой строки может быть массив "детей":
const data = [
{
id: 1,
name: 'Иван',
children: [
{ id: 4, name: 'Иван Младший', age: 7 }
]
},
// …
]
Теперь указываем в таблице функцию получения подстрок:
const table = useVueTable({
data,
columns,
getSubRows: row => row.children, // Подсказываю таблице, где искать подстроки
})
В шаблоне добавляем рекурсивный вывод строк:
<tr v-for="row in table.getRowModel().rows" :key="row.id">
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
{{ cell.getValue() }}
</td>
<!-- Здесь рекурсивно рендерим потомков -->
<template v-if="row.getIsExpanded()">
<tr v-for="subRow in row.getSubRows()" :key="subRow.id">
<!-- Аналогично выводим содержимое -->
</tr>
</template>
</tr>
Для управления раскрытием строк используйте row.toggleExpanded() или row.getIsExpanded().
Ленивая подгрузка (виртуализация) и работа с большими данными
Для таблицы с тысячами строк важно быстро отображать только видимые данные. TanStack Table интегрируется с библиотеками виртуализации (например, @tanstack/vue-virtual).
Основы виртуализации таблицы
- Установите виртуализатор:
npm install @tanstack/vue-virtual
- Импортируйте и используйте RowVirtualizer:
import { useVirtualizer } from '@tanstack/vue-virtual'
- Примените виртуализацию к tbody:
<tbody
ref="tbody"
:style="{ height: virtualizer.getTotalSize() + 'px', position: 'relative' }"
>
<tr
v-for="virtualRow in virtualizer.getVirtualItems()"
:key="virtualRow.index"
:style="{ position: 'absolute', top: virtualRow.start + 'px' }"
>
<!-- Выводите ячейки по примеру выше -->
</tr>
</tbody>
Поясняю: эта схема рендерит только те строки, которые попадут во viewport, экономя ресурсы браузера на больших массивах.
Кастомизация стилей и интеграция с UI библиотеками
TanStack Table не предоставляет готовый UI — здесь вы сами выбираете, как стилизовать и организовать дизайн. Это помогает:
- Интегрировать таблицу в любой UI-фреймворк (например, Vuetify, Element Plus, PrimeVue)
- Добавить свои компоненты (например, выпадающие меню, чекбоксы, инпуты, кастомные сортировщики и фильтры).
Используйте слоты Vue и условные конструкции для расширения функционала без ограничения возможностями готового компонента.
Лучшие практики и советы
- Используйте key-prop в v-for только для уникальных идентификаторов.
- Храните данные таблицы отдельно от компонентного состояния (например, через Pinia или Vuex) — так будет проще обновлять и масштабировать интерфейс.
- Минимизируйте количество рендеров при изменении данных — используйте виртуализацию для больших таблиц.
- Для сложных фильтров и сортировок реализуйте кастомные функции в определении колонки через параметры
sortType
иfilterFn
. - Пользуйтесь типами данных (TypeScript) — TanStack Table полностью их поддерживает.
Заключение
TanStack Table дает мощный набор инструментов для работы с таблицами во Vue — от простых статических таблиц до сложных динамических интерфейсов с фильтрацией, сортировкой, пагинацией и поддержкой больших объемов данных. Вы полностью управляете разметкой и внешним видом, а библиотека заботится о бизнес-логике работы таблицы. Это позволяет легко создавать кастомные и производительные таблицы для любых задач, не ограничиваясь готовыми решениями.
Частозадаваемые технические вопросы по теме статьи и ответы на них
Как сделать редактируемые ячейки в TanStack Table во Vue?
Чтобы реализовать редактируемые ячейки, используйте scoped slots или возвращайте функции/компоненты в cell-рендере столбца. Для каждого cell используйте v-if для переключения между режимом вывода и редактирования. Для управления значениями используйте v-model и обработчики событий для изменения данных.
Можно ли подключить TanStack Table к серверной пагинации/фильтрации?
Да, просто реализуйте соответствующие обработчики на стороне сервера. Вынесите свойство data в computed или watch, чтобы при изменениях параметров пагинации, фильтрации или сортировки запрашивать новые данные с сервера и обновлять их в таблице через setData().
Как сделать drag-n-drop колонок или строк?
TanStack Table не включает drag-n-drop «из коробки», но позволяет вставлять любые UI-библиотеки для drag-n-drop в вашу обёртку и обновлять порядок колонок (например, через обновление массива columns).
Как реализовать закрепление (freezing) колонок или строк?
В TanStack Table есть колонки с параметрами pin: 'left' или pin: 'right'. Для закрепления используйте эти параметры в описании columns и стилизуйте закрепленные столбцы через CSS (например, position sticky).
Что делать при ошибке "Cannot read property 'headers' of undefined"?
Проверьте, что переданы корректные данные и колонки, а таблица инициализирована до первого рендера шаблона. Обычно ошибка указывает на отсутствие или неправильную структуру columns/data. Используйте проверки и ensure before rendering.