Real User Monitoring

Real User Monitoring (RUM) — это сбор и анализ данных о фактическом поведении пользователей в реальном времени. В веб-приложениях на Lit RUM используется для измерения производительности, стабильности и пользовательского опыта непосредственно в браузере, а не в лабораторных условиях. Такой подход позволяет фиксировать реальные задержки рендеринга, проблемы с сетью, ошибки выполнения и особенности взаимодействия с интерфейсом.

Lit, как библиотека для создания легковесных веб-компонентов, хорошо подходит для внедрения RUM благодаря чёткой модели жизненного цикла, реактивности и изоляции компонентов.


Ключевые метрики RUM

В приложениях на Lit чаще всего собираются следующие группы метрик:

Производительность загрузки

  • TTFB (Time To First Byte) — время ответа сервера.
  • FCP (First Contentful Paint) — момент появления первого визуального контента.
  • LCP (Largest Contentful Paint) — время отрисовки самого крупного элемента.
  • CLS (Cumulative Layout Shift) — стабильность макета.
  • INP / FID — задержка обработки пользовательского ввода.

Производительность компонентов

  • Время инициализации веб-компонента.
  • Длительность первого рендера (firstUpdated).
  • Количество повторных обновлений (update / updated).
  • Задержки при асинхронной загрузке данных.

Надёжность

  • Ошибки JavaScript.
  • Необработанные промисы.
  • Ошибки сетевых запросов.
  • Ошибки в пользовательских обработчиках событий.

Инструменты браузера как основа RUM

Lit не предоставляет встроенного RUM-механизма, поэтому используются стандартные Web API:

Performance API

Используется для сбора таймингов:

  • performance.getEntriesByType('navigation')
  • performance.getEntriesByType('paint')
  • PerformanceObserver для LCP, CLS, INP

Error API

  • window.onerror
  • window.addEventListener('error')
  • window.addEventListener('unhandledrejection')

Network Information API (опционально)

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


Встраивание RUM в архитектуру Lit

Глобальный слой мониторинга

На уровне приложения создаётся отдельный модуль, отвечающий за:

  • инициализацию наблюдателей,
  • агрегацию данных,
  • отправку на сервер.

Этот слой не должен зависеть от конкретных компонентов.

export const rum = {
  send(event) {
    navigator.sendBeacon('/rum', JSON.stringify(event));
  }
};

Измерение жизненного цикла компонентов

Lit предоставляет чёткие хуки, которые удобно использовать для RUM.

Фиксация времени инициализации

class UserCard extends LitElement {
  connectedCallback() {
    this._connectedAt = performance.now();
    super.connectedCallback();
  }

  firstUpdated() {
    const duration = performance.now() - this._connectedAt;
    rum.send({
      type: 'component-init',
      name: 'user-card',
      duration
    });
  }
}

Отслеживание повторных обновлений

updated(changedProps) {
  rum.send({
    type: 'component-update',
    name: 'user-card',
    props: [...changedProps.keys()]
  });
}

Реактивность и влияние на метрики

Каждое изменение реактивного свойства вызывает обновление шаблона. В контексте RUM важно:

  • выявлять избыточные обновления,
  • отслеживать цепочки изменений,
  • фиксировать длительные обновления.
performUpdate() {
  const start = performance.now();
  super.performUpdate();
  const duration = performance.now() - start;

  if (duration > 16) {
    rum.send({
      type: 'slow-update',
      component: this.tagName.toLowerCase(),
      duration
    });
  }
}

Мониторинг асинхронных операций

Большинство компонентов Lit работают с асинхронными данными. Для RUM важно измерять:

  • время запроса,
  • время до появления данных в DOM.
async loadData() {
  const start = performance.now();
  const data = await fetch('/api/user').then(r => r.json());
  this.user = data;

  rum.send({
    type: 'data-load',
    component: 'user-card',
    duration: performance.now() - start
  });
}

Отслеживание пользовательских действий

RUM фиксирует не только технические, но и поведенческие данные:

  • клики,
  • ввод,
  • навигацию.

В Lit это делается через обработчики событий.

handleClick() {
  rum.send({
    type: 'interaction',
    component: 'user-card',
    action: 'click'
  });
}

Ошибки внутри компонентов

Ошибки в шаблонах и методах компонентов особенно критичны, так как могут нарушить изоляцию UI.

try {
  this.render();
} catch (e) {
  rum.send({
    type: 'component-error',
    component: 'user-card',
    message: e.message,
    stack: e.stack
  });
}

Контекст пользователя и сессии

Для анализа RUM-данных необходим контекст:

  • идентификатор сессии,
  • версия приложения,
  • платформа и браузер,
  • язык интерфейса.

Контекст добавляется централизованно при отправке данных.

rum.send({
  type: 'metric',
  name: 'LCP',
  value,
  context: {
    sessionId,
    appVersion,
    locale
  }
});

Оптимизация объёма данных

RUM не должен ухудшать производительность приложения. Применяются:

  • семплирование,
  • агрегация,
  • отправка через sendBeacon,
  • отложенная отправка при visibilitychange.

Связь RUM и архитектуры Lit

Грамотно внедрённый RUM:

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

Lit с его декларативностью и прозрачным жизненным циклом позволяет внедрять Real User Monitoring без жёсткого вмешательства в бизнес-логику, сохраняя читаемость и поддержку кода.