Анализ производительности

Производительность библиотеки определяется количеством DOM-элементов с автообновлением, частотой таймеров и стоимостью каждого обновления. Понимание этих факторов позволяет применять библиотеку без деградации интерфейса.


Стоимость одного обновления

Каждое срабатывание таймера:

  1. Вычисляет Date.now() — очень дёшево.
  2. Вызывает функцию локали — крайне дёшево.
  3. Записывает строку в element.textContent — вызывает reflow при изменении.

Запись в textContent дорогостоящая только если меняется — если значение не изменилось, дополнительного reflow нет. Библиотека всегда записывает новое значение, не проверяя старое.


Оптимизация: проверка перед записью

import { format } from 'timeago.js';

function updateIfChanged(el, locale = 'ru') {
  const date = el.getAttribute('datetime');
  const newText = format(date, locale);
  if (el.textContent !== newText) {
    el.textContent = newText;
  }
}

Оценка нагрузки по количеству элементов

Элементов Таймеров (прим.) Нагрузка
10 10 Незаметна
100 100 Незначительная
1000 1000 Ощутимая на слабых устройствах
10000 10000 Критическая

Адаптивные интервалы снижают нагрузку

Встроенный механизм render автоматически замедляет обновление для старых дат:

  • “5 секунд назад” → таймер каждые 10 секунд
  • “5 дней назад” → таймер раз в день

В результате большинство элементов на реальной странице обновляются редко.


Профилирование в Chrome DevTools

  1. Открыть DevTools → Performance.
  2. Нажать Record, подождать 30 секунд.
  3. Остановить запись.
  4. В треке Main найти “Timer Fired” — это таймеры timeago.
  5. Проверить суммарное время CPU, занятое обновлениями.

Если таймеры занимают > 1% CPU в idle-режиме — нагрузка избыточна.


Flame graph для анализа

При профилировании можно увидеть цепочку вызовов:

Timer Fired
  └─ (timeago internal)
      └─ format
          └─ localeFunc
              └─ textContent = "..."

Сравнение подходов по производительности

Подход CPU Перерисовки Автообновление
format разово Нет При рендере Нет
format + setInterval Низкое Регулярно Да
render (встроен в timeago) Низкое Адаптивно Да
render + IntersectionObserver Минимальное Только для видимых Да

Бенчмарк форматирования

Функция format обрабатывает тысячи вызовов в секунду:

const iterations = 100000;
const start = performance.now();

for (let i = 0; i < iterations; i++) {
  format(Date.now() - i * 1000, 'ru');
}

const elapsed = performance.now() - start;
console.log(`${iterations} вызовов за ${elapsed.toFixed(2)}ms`);
// Обычно: 100000 вызовов за 50-200ms

Анализ потребления памяти

Каждый элемент, переданный в render, создаёт запись в WeakMap. Это ~100–200 байт на элемент. Для 1000 элементов — ~200 КБ, что незначительно.

Критично: если элементы удаляются без cancel, WeakMap не освобождается немедленно (зависит от GC).


Инструменты для оценки

  • Chrome Performance Tab — анализ таймеров и перерисовок.
  • Chrome Memory Tab — Heap Snapshot для утечек.
  • Lighthouse — Total Blocking Time как интегральная метрика.
  • Web Vitals — CLS (Cumulative Layout Shift) если обновление меняет размер элемента.
  • performance.now() — точный замер времени форматирования.

CLS и размер элемента

Если текст при обновлении меняет длину (например, “только что” → “1 минуту назад”), это может вызвать Layout Shift. Для предотвращения:

.time-ago {
  min-width: 8ch; /* Резервировать минимальное место */
  display: inline-block;
}