Оптимизация обновлений

Частые обновления DOM — основной источник производительностных проблем при использовании timeago.js. Несколько стратегий позволяют снизить нагрузку без потери функциональности.


Стратегия 1: обновлять только изменившиеся значения

По умолчанию библиотека записывает строку при каждом срабатывании таймера. Обёртка с проверкой:

import { format } from 'timeago.js';

function smartUpdate(el, locale = 'ru') {
  const date    = el.getAttribute('datetime');
  const current = el.textContent;
  const next    = format(date, locale);

  if (current !== next) {
    el.textContent = next;
  }
}

Стратегия 2: адаптивный интервал обновления

import { format } from 'timeago.js';

function adaptiveRenderer(elements, locale = 'ru') {
  function getDelay(date) {
    const diff = Math.abs(Date.now() - new Date(date).getTime());
    if (diff < 60000)    return 10000;    // < 1 мин
    if (diff < 3600000)  return 60000;    // < 1 ч
    if (diff < 86400000) return 1800000;  // < 1 д
    return 86400000;                       // > 1 д
  }

  function upd ate() {
    let minDelay = Infinity;

    elements.forEach(el => {
      const date = el.getAttribute('datetime');
      if (!date) return;

      const next = format(date, locale);
      if (el.textContent !== next) el.textContent = next;

      minDelay = Math.min(minDelay, getDelay(date));
    });

    setTimeout(update, minDelay);
  }

  update();
}

Стратегия 3: IntersectionObserver

Запускать таймеры только для видимых элементов:

import { render, cancel } from 'timeago.js';

function visibilityOptimizedRender(nodes, locale = 'ru') {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(({ target, isIntersecting }) => {
      if (isIntersecting) {
        render(target, locale);
      } else {
        cancel(target);
      }
    });
  });

  nodes.forEach(node => observer.observe(node));

  return () => {
    observer.disconnect();
    cancel(nodes);
  };
}

Стратегия 4: requestAnimationFrame для батчинга

Группировать обновления в один кадр:

import { format } from 'timeago.js';

let scheduled = false;
const pending  = new Se t();

function scheduleUpdate(el) {
  pending.add(el);

  if (!scheduled) {
    scheduled = true;
    requestAnimationFrame(() => {
      pending.forEach(el => {
        const date = el.getAttribute('datetime');
        if (!date) return;

        const next = format(date, 'ru');
        if (el.textContent !== next) el.textContent = next;
      });

      pending.clear();
      scheduled = false;
    });
  }
}

Стратегия 5: заморозка для старых дат

Прекращать обновления для дат, которые уже не изменятся в читаемом виде:

import { render, cancel, format } from 'timeago.js';

function frozenRender(el, locale = 'ru') {
  const date  = el.getAttribute('datetime');
  const ageMs = Date.now() - new Date(date).getTime();

  // Если дата старше года — не нужен таймер
  if (ageMs > 365 * 86400000) {
    el.textContent = format(date, locale);
    return;
  }

  render(el, locale);
}

Стратегия 6: Web Worker для вычислений

Перенести расчёты в фоновый поток (подходит для очень большого количества дат):

// worker.js
import { format } from 'timeago.js';

self.onmess age = ({ data }) => {
  const { dates, locale } = data;
  const results = dates.map(d => format(d, locale));
  self.postMessage(results);
};
// main.js
const worker = new Worker('./worker.js');

worker.postMessage({ dates: allDates, locale: 'ru' });

worker.onmess age = ({ data }) => {
  elements.forEach((el, i) => {
    el.textContent = data[i];
  });
};

Стратегия 7: virtualization

Рендерить только элементы в видимой области (react-window, vue-virtual-scroller):

import { FixedSizeList } from 'react-window';
import { format } from 'timeago.js';

function Row({ index, style, data }) {
  return (
    <div style={style}>
      <time>{format(data[index].createdAt, 'ru')}</time>
    </div>
  );
}

function VirtualFeed({ posts }) {
  return (
    <FixedSizeList
      height={600}
      itemCount={posts.length}
      itemSize={80}
      itemData={posts}
    >
      {Row}
    </FixedSizeList>
  );
}

Таблица оптимизаций

Стратегия Снижение CPU Сложность реализации
Проверка перед записью 20–30% Низкая
Адаптивный интервал 30–50% Средняя
IntersectionObserver 50–80% Средняя
rAF-батчинг 20–40% Средняя
Заморозка старых дат 10–30% Низкая
Виртуализация 80–95% Высокая