Тяжёлая сортировка без просадки UI: Worker Kit + Virtual Scroller Kit
Список на 100 000+ строк сам по себе не проблема — Virtual Scroller Kit рендерит только видимые строки. Проблема начинается, когда список ещё и сортируется или фильтруется на каждое изменение: Array.prototype.sort на массиве такого размера — это синхронная, блокирующая UI работа на основном потоке, и async/await её не спасает — сам цикл сортировки всё равно выполняется в том же потоке, что рисует интерфейс.
Worker Kit выносит саму сортировку в честный Worker-поток, а VirtualList рендерит уже готовый результат.
Воркер
// sort-rows.worker.ts
import { defineWorkerHandler } from 'vue-worker-kit/worker'
interface Row {
id: number
text: string
score: number
}
export default defineWorkerHandler(async (data: Row[], ctx) => {
return data.sort((a, b) => b.score - a.score)
})Компонент
<script setup lang="ts">
import { ref } from 'vue'
import { useWorker } from 'vue-worker-kit'
import { VirtualList } from 'vue-virtual-scroller-kit'
interface Row {
id: number
text: string
score: number
}
const { run, isRunning } = useWorker<typeof import('./sort-rows.worker')>(
() => new Worker(new URL('./sort-rows.worker.ts', import.meta.url), { type: 'module' }),
)
const rows = ref<Row[]>([])
async function resort(source: Row[]) {
// sorted: Row[] — тип выведен из sort-rows.worker.ts, дженерик не нужен
rows.value = await run(source)
}
</script>
<template>
<p v-if="isRunning">Сортируем…</p>
<VirtualList :items="rows" :estimated-item-size="48" style="height: 600px">
<template #default="{ item }">
<div style="padding: 12px 16px; border-bottom: 1px solid #eee">{{ item.text }}</div>
</template>
</VirtualList>
</template>Почему это разумно именно вместе, а не отдельно
VirtualList сам по себе не ускоряет сортировку — он ускоряет только рендер. Без воркера интерфейс всё равно "подвиснет" на время sort(), просто список после этого будет длинным, но быстрым. Воркер сам по себе тоже не обязателен для маленьких списков — цена postMessage и структурного клонирования данных туда-обратно реальна, и для списка в 500 строк обычная сортировка на основном потоке отработает быстрее. Пара имеет смысл именно на масштабе, где проблема реальна: десятки-сотни тысяч строк, которые к тому же пересортировываются часто (по клику на заголовок колонки, при вводе в поиск).
Что дальше
- Для по-настоящему больших наборов, обрабатываемых пачками, —
createWorkerPool()вместо одногоuseWorker(). См. Worker Kit — обзор. - Для табличного, а не просто построчного вида —
VirtualTableвместоVirtualList, тот же принцип. См. Virtual Scroller Kit — обзор.