Табличные интерфейсы являются одним из наиболее ресурсоёмких
компонентов пользовательского интерфейса. Библиотека Element Plus
предоставляет компонент el-table, который позволяет
отображать большие объёмы структурированных данных, однако при работе с
крупными наборами данных возникает проблема производительности.
Каждая строка таблицы представляет собой компонент Vue, содержащий реактивные зависимости, вычисления и DOM-узлы. При увеличении количества строк:
Поэтому при разработке интерфейсов с большими таблицами необходимо применять специальные техники оптимизации.
Основные направления оптимизации:
Vue отслеживает изменения всех реактивных свойств. Если таблица содержит тысячи строк, каждое изменение приводит к перерасчёту реактивной цепочки.
shallowRefКогда данные таблицы представляют собой большой массив, часто нет необходимости отслеживать изменения каждого вложенного свойства.
import { shallowRef } from 'vue'
const tableData = shallowRef([])
В этом случае Vue отслеживает только замену массива, а не изменения внутри элементов.
Преимущества:
Обновление данных выполняется через замену массива:
tableData.value = newData
markRawЕсли объекты данных не требуют реактивности, их можно исключить из системы реактивности Vue.
import { markRaw } from 'vue'
const data = markRaw(largeDataset)
Это особенно полезно при работе с данными, полученными из API и не изменяющимися на клиенте.
Каждая строка таблицы должна иметь стабильный ключ. В Element Plus
это задаётся через свойство row-key.
<el-table
:data="tableData"
row-key="id"
>
</el-table>
Причины использования row-key:
Без ключа Vue может заново пересоздавать строки, что значительно ухудшает производительность.
Одним из наиболее эффективных способов оптимизации является ограничение количества одновременно отображаемых строк.
В Element Plus используется компонент el-pagination.
<el-table :data="pagedData">
<el-table-column prop="name" label="Name"/>
<el-table-column prop="age" label="Age"/>
</el-table>
<el-pagination
layout="prev, pager, next"
:total="total"
:page-size="pageSize"
@current-change="handlePageChange"
/>
Логика обработки страниц:
const page = ref(1)
const pageSize = 20
const pagedData = computed(() => {
const start = (page.value - 1) * pageSize
return tableData.value.slice(start, start + pageSize)
})
function handlePageChange(newPage) {
page.value = newPage
}
Преимущества:
Когда необходимо отображать тысячи строк без пагинации, используется виртуализация.
Виртуализация означает, что в DOM одновременно находятся только видимые строки.
Element Plus предоставляет компонент:
el-table-v2
Он реализует виртуальный скролл.
<el-table-v2
:columns="columns"
:data="data"
:width="1000"
:height="600"
/>
Описание параметров:
| Параметр | Назначение |
|---|---|
| columns | конфигурация колонок |
| data | массив строк |
| width | ширина таблицы |
| height | высота области прокрутки |
const columns = [
{
key: 'id',
dataKey: 'id',
title: 'ID',
width: 100
},
{
key: 'name',
dataKey: 'name',
title: 'Name',
width: 200
},
{
key: 'email',
dataKey: 'email',
title: 'Email',
width: 300
}
]
Виртуализированная таблица работает по следующему принципу:
Это позволяет работать даже с десятками тысяч строк.
Каждая колонка может содержать пользовательский шаблон. Сложные шаблоны увеличивают стоимость рендеринга.
<el-table-column label="User">
<template #default="scope">
<div class="user">
<img :src="scope.row.avatar">
<span>{{ scope.row.name }}</span>
<span>{{ scope.row.email }}</span>
</div>
</template>
</el-table-column>
При большом количестве строк это приводит к созданию множества DOM-узлов.
Оптимизированный вариант:
<el-table-column
prop="name"
label="User"
/>
Минимизация вложенности:
Element Plus поддерживает ленивую загрузку строк в древовидных таблицах.
<el-table
:data="tableData"
lazy
:load="loadChildren"
row-key="id"
>
</el-table>
Функция загрузки:
function loadChildren(row, treeNode, resolve) {
api.getChildren(row.id).then(data => {
resolve(data)
})
}
Преимущества:
Иногда таблица обновляется слишком часто, например при фильтрации или поиске.
import { debounce } from 'lodash'
const updateFilter = debounce((value) => {
filter.value = value
}, 300)
Это уменьшает количество перерисовок.
Вычисляемые свойства могут пересчитываться слишком часто.
Неоптимальный вариант:
const filteredData = computed(() => {
return tableData.value.filter(row => row.age > minAge.value)
})
Если таблица содержит тысячи строк, фильтрация выполняется каждый раз при изменении зависимости.
Оптимизация:
Если строки таблицы не изменяются, можно использовать кэширование компонентов.
В Vue применяется директива v-memo.
<template v-memo="[row.id]">
<RowComponent :row="row"/>
</template>
Преимущества:
Фиксированная высота строк значительно ускоряет виртуализацию.
<el-table-v2
:row-height="40"
/>
Если высота строк динамическая, библиотека вынуждена выполнять дополнительные вычисления.
Некоторые возможности таблицы увеличивают нагрузку:
Если они не используются, их лучше не активировать.
Например, отключение сортировки:
<el-table-column
prop="name"
label="Name"
sortable="false"
/>
Для очень больших таблиц (десятки тысяч записей) оптимальной стратегией является перенос вычислений на сервер.
На сервере выполняются:
На клиент передаётся только необходимая часть данных.
Пример запроса:
async function fetchData(page, pageSize) {
const response = await api.get('/users', {
params: {
page,
pageSize
}
})
tableData.value = response.data.items
}
table-layout="fixed"Фиксированная схема таблицы ускоряет расчёт ширины колонок браузером.
<el-table
:data="tableData"
table-layout="fixed"
>
</el-table>
Преимущества:
Большое количество колонок также влияет на производительность.
Каждая колонка добавляет:
Оптимальные стратегии:
v-onceЕсли строка никогда не изменяется, можно использовать директиву
v-once.
<div v-once>
{{ row.name }}
</div>
Элемент будет отрендерен только один раз.
Для анализа производительности используются инструменты:
Основные показатели:
Профилирование помогает выявить:
При проектировании интерфейсов с большими таблицами обычно применяется комбинация методов:
Такой подход позволяет поддерживать высокую производительность даже при работе с десятками тысяч записей и сложными интерфейсами управления данными.