Производительность
Движок рассчитан на сотни блоков и соединений и на блоки, которые приходят и уходят, — свёрнутые узлы деревьев, строки виртуализированных списков.
Что он делает за вас
- Перерисовывается только изменившееся. Проход измеряет блоки, а затем переписывает те линии, у которых действительно изменились геометрия или вид. Пути и DOM-узлы остальных используются повторно. Перетаскивание блока с 20 линиями среди 300 затрагивает эти 20.
- Чтение и запись разделены. Каждый прямоугольник измеряется один раз за кадр, до любой записи в DOM; размеры подписей кэшируются. Принудительных пересчётов раскладки посреди прохода нет.
- Блок без соединений ничего не стоит. Он не наблюдается через
ResizeObserverи не измеряется. Его регистрируют, когда появляется первое соединение (или когда он может начать или закончить его в режиме создания связей). - Вывод не меняется. При настройках по умолчанию SVG байт-в-байт такой же, как рисовал 0.4.3.
Пакетное обновление
batch(fn) выполняет fn и затем рисует один раз, сколько бы блоков, соединений или изменений конфигурации там ни было:
linker.batch(() => {
for (const id of expanded) linker.addConnection(links[id])
linker.setConfig({ lines: { width: 3 } })
})<VisualLinker> и useVisualLinker() уже пакетируют собственные обновления: одно обновление реестра за тик для блоков, помеченных директивами, и в движок передаются только изменившиеся соединения.
getStats()
linker.getStats()
// { blocks, connections, rendered, culled, proxied, lastRenderMs, medianRenderMs, observedElements }blocks,connections— сколько зарегистрировано.rendered— сколько соединений записано в DOM за последний проход.culled,proxied— скрыто отсечением, нарисовано к запасному блоку.lastRenderMs,medianRenderMs— время последнего прохода и медиана по последним 60.observedElements— элементов подResizeObserver, включая контейнер.
Метод ничего не измеряет в DOM, поэтому его безопасно вызывать на каждом кадре. В Vue stats() отдаёт компонент и возвращает useVisualLinker(); он не реактивный.
Группа performance
cull—boolean | { margin?: number }· по умолчанию:false. Соединения, которых не видно, не рисуются: ни один конец и ни один участок между ними не доходит до видимой области (контейнера, его прокручиваемых предков и окна), расширенной наmarginpx (по умолчанию64) и на запас под изгиб. Отсечённое соединение остаётся в модели, записи вlayoutу него нет, а когда оно возвращается, рисуется снова.maxConnectionsForJumps—number· по умолчанию:150. Когда мостики просят больше этого числа соединенийsmoothstep, мостики отключаются: поиск пересечений растёт вместе с числом отрезков.maxRoutingNodes—number· по умолчанию:6000. Размер сетки поиска маршрутизации в обход блоков, выше которого линия остаётся на обычном маршруте.maxObstacleBlocks—number· по умолчанию:200. Если вокруг соединения больше блоков, его не обходят.
Замеры
pnpm test:perf в репозитории запускает стенд playground/bench.html в Chromium и пишет docs/PERFORMANCE.md; при превышении порога он падает. Chromium, headless, ноутбук, без троттлинга; время в мс, один refresh() без изменений, если не сказано иное.
| Сценарий | 0.4.3 | 0.4.5 | Порог |
|---|---|---|---|
| 100 блоков, 100 связей, медиана / p95 | 2,1 / 2,8 | 0,7 / 1,3 | ≤ 4 / ≤ 8 |
| 300 блоков, 300 связей, медиана | 5,7 | 2,2 | ≤ 12 |
| FPS при перетаскивании блока с 20 линиями, 300 блоков | 59,8 | 60,6 | ≥ 55 |
| FPS при прокрутке колонок по 200 блоков, 100 линий | 60,9 | 60,3 | ≥ 55 |
| Первая отрисовка, 1000 блоков, 500 связей | 47 | 39 | ≤ 400 |
| 1000 блоков без связей: наблюдаемых элементов | 1001 | 1 | 1 |
| Тяжёлое оформление¹, 100 / 100, медиана | 10,0 | 2,6 | — |
| Тяжёлое оформление¹, 300 / 300, медиана | 41,0 | 5,9 | — |
| Тяжёлое оформление¹, 300 / 300, FPS при перетаскивании | 13,5 | 48,6 | — |
¹ smoothstep, обход блоков, мостики, подпись и стрелка на каждой линии, видимые разведённые порты. Вид по умолчанию укладывался в целевые показатели и в 0.4.3; выигрыш там, где включены дорогие режимы.
Советы
- На больших диаграммах включите
performance.cullи оставьтеclipToScrollParents. - Для множества линий выбирайте
bezierилиstraight;smoothstepс обходом блоков и мостиками — самое дорогое сочетание. - Сворачивайте деревья через запасные концы, а не удаляя и добавляя соединения заново.
- При прямой работе с движком группируйте изменения через
batch().