SvelteKit предоставляет широкие возможности для построения динамических интерфейсов с таблицами и списками, используя компонентный подход и реактивность, присущую Svelte. Основное внимание уделяется управлению состоянием данных, производительности рендеринга и интеграции с внешними библиотеками UI.
В SvelteKit таблицы обычно создаются с использованием стандартных
HTML-элементов <table>, <thead>,
<tbody> и <tr>, но с реактивной
привязкой данных через синтаксис {#each}. Пример базовой
таблицы:
<script>
let users = [
{ id: 1, name: 'Иван', age: 28 },
{ id: 2, name: 'Мария', age: 32 },
{ id: 3, name: 'Алексей', age: 24 }
];
</script>
<table class="table-auto w-full border-collapse border border-gray-300">
<thead>
<tr class="bg-gray-100">
<th class="border p-2 text-left">ID</th>
<th class="border p-2 text-left">Имя</th>
<th class="border p-2 text-left">Возраст</th>
</tr>
</thead>
<tbody>
{#each users as user}
<tr class="hover:bg-gray-50">
<td class="border p-2">{user.id}</td>
<td class="border p-2">{user.name}</td>
<td class="border p-2">{user.age}</td>
</tr>
{/each}
</tbody>
</table>
Ключевые моменты:
{#each} обеспечивает автоматический
рендер при изменении массива.setState.Для интерактивных таблиц часто требуется сортировка и фильтрация. Это можно реализовать через реактивные переменные и вычисляемые значения:
<script>
let sortKey = 'name';
let sortAsc = true;
$: sortedUsers = [...users].sort((a, b) => {
if(a[sortKey] < b[sortKey]) return sortAsc ? -1 : 1;
if(a[sortKey] > b[sortKey]) return sortAsc ? 1 : -1;
return 0;
});
function toggleSort(key) {
if (sortKey === key) sortAsc = !sortAsc;
else { sortKey = key; sortAsc = true; }
}
</script>
<table class="table-auto w-full border border-gray-300">
<thead>
<tr>
<th class="border p-2 cursor-pointer" on:click={() => toggleSort('id')}>ID</th>
<th class="border p-2 cursor-pointer" on:click={() => toggleSort('name')}>Имя</th>
<th class="border p-2 cursor-pointer" on:click={() => toggleSort('age')}>Возраст</th>
</tr>
</thead>
<tbody>
{#each sortedUsers as user}
<tr>
<td class="border p-2">{user.id}</td>
<td class="border p-2">{user.name}</td>
<td class="border p-2">{user.age}</td>
</tr>
{/each}
</tbody>
</table>
Особенности реализации:
$: создаёт реактивное вычисление
sortedUsers, которое автоматически обновляется при
изменении массива users или ключа сортировки.toggleSort позволяет переключать направление
сортировки без перерендеринга всей таблицы.Для больших наборов данных пагинация уменьшает нагрузку на DOM. В SvelteKit это реализуется через реактивные диапазоны:
<script>
let currentPage = 1;
let itemsPerPage = 5;
$: totalPages = Math.ceil(users.length / itemsPerPage);
$: paginatedUsers = users.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
function nextPage() {
if(currentPage < totalPages) currentPage += 1;
}
function prevPage() {
if(currentPage > 1) currentPage -= 1;
}
</script>
<table class="table-auto w-full border">
<thead>
<tr>
<th>ID</th>
<th>Имя</th>
<th>Возраст</th>
</tr>
</thead>
<tbody>
{#each paginatedUsers as user}
<tr>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.age}</td>
</tr>
{/each}
</tbody>
</table>
<div class="flex justify-between mt-2">
<button on:click={prevPage} disabled={currentPage === 1}>Назад</button>
<span>Страница {currentPage} из {totalPages}</span>
<button on:click={nextPage} disabled={currentPage === totalPages}>Вперед</button>
</div>
slice эффективно разбивает массив на страницы без
создания новых массивов.Списки в SvelteKit строятся аналогично таблицам, с применением
{#each} для генерации элементов <ul> или
<ol>:
<script>
let tasks = [
{ id: 1, title: 'Сделать домашку', done: false },
{ id: 2, title: 'Купить продукты', done: true },
{ id: 3, title: 'Позвонить другу', done: false }
];
function toggleDone(task) {
task.done = !task.done;
}
function removeTask(id) {
tasks = tasks.filter(t => t.id !== id);
}
</script>
<ul class="list-disc pl-5">
{#each tasks as task (task.id)}
<li class="flex justify-between items-center mb-1">
<span class={task.done ? 'line-through text-gray-500' : ''}>{task.title}</span>
<div class="space-x-2">
<button on:click={() => toggleDone(task)}>{task.done ? 'Вернуть' : 'Выполнено'}</button>
<button on:click={() => removeTask(task)}>Удалить</button>
</div>
</li>
{/each}
</ul>
Выделенные моменты:
(task.id) используется как ключ для оптимизации
рендеринга при изменении массива.line-through и
text-gray-500 визуально выделяет выполненные элементы.Библиотеки, такие как Svelte Material UI, Flowbite Svelte, SvelteStrap, позволяют создавать таблицы и списки с расширенной функциональностью: сортировка, фильтрация, пагинация, чекбоксы и модальные окна. Пример с Svelte Material UI:
<script>
import { DataTable, DataTableHeader, DataTableRow, DataTableCell } from 'svelte-material-ui';
let products = [
{ id: 1, name: 'Ноутбук', price: 1200 },
{ id: 2, name: 'Смартфон', price: 800 }
];
</script>
<DataTable>
<DataTableHeader>
<DataTableCell>ID</DataTableCell>
<DataTableCell>Название</DataTableCell>
<DataTableCell>Цена</DataTableCell>
</DataTableHeader>
{#each products as product}
<DataTableRow>
<DataTableCell>{product.id}</DataTableCell>
<DataTableCell>{product.name}</DataTableCell>
<DataTableCell>{product.price}</DataTableCell>
</DataTableRow>
{/each}
</DataTable>
(item.id) в {#each} для
предотвращения лишнего перерендеринга.$: для фильтров и
сортировки, чтобы минимизировать ручное обновление состояния.