Статьи

Пять быстрых таблиц данных JavaScript — обзор производительности

Пример сетки данных.

Трудно представить современное веб-приложение, которое не включает компонент сетки данных для отображения и фильтрации данных. В этой статье мы сравниваем производительность некоторых из самых популярных на рынке компонентов сетки JavaScript, измеряя как начальное время рендеринга, так и производительность прокрутки. Мы также описываем используемую методологию, и есть репозиторий GitHub, где вы можете увидеть используемый код.


Вам также могут понравиться:
Примеры гридов данных, вычислительных гридов, сервисных гридов и выполнение запросов SQL

Компоненты сетки

В этом сравнении были использованы следующие популярные сетки данных:

Методология сравнительного анализа

Чтобы сделать сравнение как можно более справедливым, были предприняты следующие действия:

  • Для каждой сетки было создано два теста: один с заблокированными / фиксированными / закрепленными столбцами и один без
  • Один и тот же набор данных использовался для всех сеток, состоящий из 10 000 объектов JSON с десятью полями в каждом.
  • Столбцы были настроены максимально одинаково для всех сеток:

    • Десять столбцов, некоторые с пользовательскими настройками:

      • Один столбец с ячейками, цвет фона которых установлен из данных.
      • Один столбец с пользовательским индикатором выполнения.
      • Один столбец даты.
      • Один логический столбец с отображением Да / Нет.
    • Первые три столбца заблокированы / исправлены / закреплены в соответствующем контрольном примере.
  • Некоторые сетки поставляются с функциями, включенными по умолчанию, некоторые требуют, чтобы вы включили их вручную. Справедливости ради, все несущественные функции, такие как сортировка, группировка, фильтрация и т. Д. Были отключены
  • Все сетки были преобразованы в контейнер размером 1280 x 1024 px.
  • Все измерения были сделаны на MacBook Pro 13 «2016 года (Intel Core i5 с частотой 2 ГГц, 8 ГБ ОЗУ).
  • Полосы прокрутки были включены.

Измерение

JavaScripts performance.now()был использован для измерения времени в тестах.

Начальный рендеринг

Для измерения начального рендеринга были предприняты следующие шаги:

  1. Все сначала было загружено на страницу, например, CSS , JavaScript и использованный набор данных.
  2. Таймер был запущен.
  3. Экземпляр сетки был создан и заполнен набором столбцов и уже загруженным набором данных.
  4. Когда сетка была полностью отображена на экране, таймер был остановлен.

Для этого использовался небольшой служебный класс, важная часть его источника:

export class RenderTimer {
    static start({ sync = true, callback }) {
        this.start = performance.now();

        callback && callback();

        if (sync) this.stop();
    }

    static stop() {
        const elapsed = performance.now() - this.start;

        console.log(elapsed);
    }
}

И пример его использования:

// For a grid that renders sync
RenderTime.start({
  callback() {
    new Grid({
       // config...
    });
  }
});

// For a grid that renders async
RenderTimer.start({
  sync : false,
  callback() {
    new Grid({
       // config...
       onRenderDone() {
          // async rendering done
         RenderTimer.stop();
       }
    });
  }
});

скроллинг

Для измерения производительности прокрутки используются два служебных класса. Тот, который заставляет сетку прокручиваться по таймеру, и тот, который измеряет FPS. Ниже вы можете увидеть источник для скроллера:

export class Scroller {
    static scroll({ element, distance = 50000, speed = 5, maxSpeed = 1000, acceleration = 1, callback, scrollFn }) {
        let scrollTop = 0;

        const intervalId = setInterval(() => {
            if (scrollFn) {
                scrollFn(scrollTop)
            } else {
                element.scrollTop = scrollTop;
            }

            scrollTop += speed;

            if (speed < maxSpeed) speed += acceleration;

            if (scrollTop > distance) {
                clearInterval(intervalId);
                callback && callback();
            }
        }, 1);
    }
}

Он либо напрямую прокручивает элемент, либо обеспечивает scrollFnобратный вызов для сеток, которые не имеют собственной прокрутки. Прокрутка начинается с 5px за обновление, ускоряясь до 1000px, таким образом имитируя прокрутку по сенсорной панели или разблокированному колесу мыши. На предопределенном расстоянии в 50 000 пикселей прокрутка останавливается и callbackвызывается.

Пример использования:

Scroller.scroll({
  element : document.querySelector('.scroller-selector'),
  callback() {
    // scrolling done 
  }
});

Обратите внимание, что программный запуск, подобный прокрутке, делает это в основном потоке, синхронно с пользовательским интерфейсом. Когда фактический пользователь прокручивает с помощью сенсорной панели, это происходит асинхронно в другом потоке. Разница может быть не очевидной, но, поскольку здесь это делается программно, пользовательский интерфейс всегда будет обновляться. Только это будет происходить быстрее или медленнее в зависимости от сетки. В реальной жизни прокрутка не блокируется обновлениями пользовательского интерфейса, что приводит к отключениям.

FPS

Для измерения FPS (кадров в секунду) используется служебный класс, который использует requestAnimationFrame. Он измеряет время, необходимое для достижения цели прокрутки, подсчитывает количество запущенных кадров и вычисляет FPS. Источник отредактирован для экономии места:

export class FPS {
    static start() {
        this.start = null;
        this.frameCount = 0;
        this.running = true;

        requestAnimationFrame(this.frameCounter);
    }

    static stop() {
        this.running = false;

        const
            elapsed = performance.now() - this.start,
            fps = this.frameCount / (elapsed / 1000);

        console.log(fps);
    }

    static frameCounter() {
        const time = performance.now();

        if (FPS.start === null) {
            FPS.prevFrameTime = FPS.start = time;
        } else {
            FPS.frameCount++;
        }

        FPS.prevFrameTime = time;

        if (FPS.running) {
            requestAnimationFrame(FPS.frameCounter)
        }
    }
}

Использование:

FPS.start();
// actions...
FPS.stop();

Полученные результаты

Сначала начальный рендеринг. Прокрутите вниз для просмотра результатов прокрутки.

Начальный рендеринг

Сначала результаты для начального рендеринга, без заблокированных / фиксированных / закрепленных столбцов:

Как видно выше, dhtmlX является самым быстрым для начальной фазы рендеринга, а Bryntum занимает второе место. Однако для всех практических применений разница между лучшим и худшим в этом случае незначительна. Реальное приложение, загружающее и отображающее 10 000 записей, будет тратить гораздо больше времени на извлечение данных из серверной части, чем время, необходимое для их отображения.

Рассматривая результаты для начального рендеринга с заблокированными / фиксированными / закрепленными столбцами, мы видим, что относительное ранжирование одинаково, но рендеринг занимает чуть больше:

ExtJS рендерит в этом случае намного медленнее, но это не заметно, если вы не измеряете его. Все еще достаточно быстро по всем направлениям.

Прокрутка FPS

Когда дело доходит до FPS, есть большие различия между сетками. Давайте сначала посмотрим на сценарий без заблокированных / фиксированных / закрепленных столбцов:

Ag-grid, Bryntum и Ext JS в значительной степени привязаны к лидерству, составляя в среднем около 60 кадров в секунду, что так же хорошо, как и получается! DevExtreme и dhtmlX отстают в 38 и 25 FPS соответственно.

В отличие от начальной производительности рендеринга, эти различия в производительности легко заметить на глаз. Быстрая прокрутка в сетке с низким FPS приводит к отключениям и плохому пользовательскому интерфейсу.

Теперь для финального раунда результатов у нас есть сетки с заблокированными / фиксированными / закрепленными столбцами. Этот случай особенно интересен для любого, кто строит или использует компоненты Scheduler или Gantt, поскольку такие компоненты всегда имеют фиксированный левый раздел с табличными данными.

Bryntum почти удается поддерживать 60 кадров в секунду, в то время как Ag-grid и ExtJS отстают на несколько кадров. Тем не менее, все еще очень быстро, спасибо разработчикам обеих библиотек. DevExtreme и dhtmlX отстают, вероятно, из-за того, что в этих сетках реализованы заблокированные секции.

Подводя итоги

В этой статье не сравниваются возможности сеток, но если вы заботитесь о производительности так же, как и мы, тогда этот пост может быть полезен при выборе используемой сетки. Bryntum Grid хорошо работает как на начальном этапе, так и во время прокрутки, но, как упоминалось выше, начальное время рендеринга достаточно хорошее для всех сторон. Имея это в виду, трудно выбрать единственного победителя, поэтому давайте назовем это связанным 1-м местом между Ag-grid, Bryntum и Ext JS.

отказ

Все измерения были выполнены одинаково для каждой сетки, и результаты сообщаются честно, но я являюсь разработчиком в Bryntum. Для прозрачности весь используемый код доступен на GitHub. Не стесняйтесь попробовать это сами: https://github.com/bryntum/grid-performance

Дальнейшее чтение

Адресация производительности приложения в приложении Java

Зачем вам нужен инструмент мониторинга производительности приложений (APM)