Набор практических рекомендаций для эффективного использования библиотеки в production-приложениях.
Если список постов не обновляется в реальном времени,
format эффективнее render:
// Хорошо для статичных списков
posts.map(post => ({
...post,
timeLabel: format(post.createdAt, 'ru'),
}));
render создаёт таймеры. Для неизменного контента это
лишние ресурсы.
useEffect(() => {
render(ref.current, 'ru');
return () => cancel(ref.current); // обязательно
}, []);
Без cancel накапливаются зависшие таймеры.
cancel(nodes);
render(nodes, newLocale);
Повторный render без cancel удваивает
количество таймеров.
Отключать таймеры для невидимых элементов:
const io = new IntersectionObserver((entries) => {
entries.forEach(({ target, isIntersecting }) => {
isIntersecting ? render(target, 'ru') : cancel(target);
});
});
nodes.forEach(n => io.observe(n));
const MAX = 100;
const nodes = Array.from(document.querySelectorAll('[datetime]')).slice(0, MAX);
render(nodes, 'ru');
react-window, react-virtuoso, vue-virtual-scroller решают проблему на уровне архитектуры. timeago обновляет только видимые элементы по умолчанию.
const next = format(date, 'ru');
if (el.textContent !== next) el.textContent = next;
Предотвращает ненужные reflow при неизменившемся значении.
function batchUpdate(elements) {
requestAnimationFrame(() => {
elements.forEach(el => {
el.textContent = format(el.getAttribute('datetime'), 'ru');
});
});
}
function frozenRender(el, locale = 'ru') {
const age = Date.now() - new Date(el.getAttribute('datetime')).getTime();
if (age > 365 * 86400000) {
el.textContent = format(el.getAttribute('datetime'), locale);
} else {
render(el, locale);
}
}
Для дат старше года значение “1 год назад” → “2 года назад” изменится только через год. Незачем обновлять каждый день.
function normalizeDate(str) {
if (!str.endsWith('Z') && !str.includes('+')) {
return str + 'Z';
}
return str;
}
Предотвращает неоднозначность при парсинге и потенциальные ошибки в расчёте разницы.
// Только в точке входа
register('ru', ru);
register('de', de);
Повторная регистрация в компонентах — лишняя работа.
На сервере не вызывать render — нет DOM. Использовать
format для начального рендера:
const timeLabel = format(post.createdAt, 'ru');
// Передать в шаблон
На клиенте при гидрации подключить render через
useEffect / onMounted.
async function loadLocale(locale) {
const mod = await import(`timeago.js/esm/lang/${locale}.js`);
register(locale, mod.default);
}
Не включать все локали в основной бандл.
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
cancel(nodes);
render(nodes, 'ru');
}
});
При длительном отсутствии пользователя таймеры мигут пропустить обновления.
const refresh = debounce(() => {
cancel(nodes);
render(nodes, 'ru');
}, 200);
Предотвращает многократный перезапуск за короткое время при загрузке данных.
cancel при размонтировании — критично.