0Pricing
Frontend Academy · Урок

Шаблоны интерфейсов реального времени

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

«Шаблоны интерфейсов реального времени» — бесплатный урок Frontend Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Frontend Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Frontend Academy содержит 4 уроков всего.

Реальное время — это ощущение мгновенного отклика

Работа в реальном времени — это не только скорость: интерфейс должен быстро реагировать, вести себя предсказуемо и устойчиво переносить задержки. Три основных подхода: оптимистические обновления, локальная очередь и интеллектуальное сравнение различий.

Оптимистические обновления

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

async function likePost(id) {
  // 1. Optimistically update UI:
  setLikes(l => ({ ...l, [id]: (l[id] || 0) + 1 }));
  setLiked(prev => ({ ...prev, [id]: true }));

  try {
    await fetch(`/api/posts/${id}/like`, { method: 'POST' });
  } catch (err) {
    // 2. Rollback on failure:
    setLikes(l => ({ ...l, [id]: (l[id] || 1) - 1 }));
    setLiked(prev => ({ ...prev, [id]: false }));
    toast.error('Could not like — please retry');
  }
}

Оптимистические обновления с временными ID

Для создаваемых элементов назначайте им локальный временный ID. Когда придёт ответ, заменяйте его настоящим ID от сервера.

async function addComment(text) {
  const tempId = `tmp-${Date.now()}`;
  setComments(c => [...c, { id: tempId, text, pending: true }]);

  try {
    const saved = await fetch('/api/comments', {
      method: 'POST',
      body: JSON.stringify({ text })
    }).then(r => r.json());

    setComments(c => c.map(m => m.id === tempId ? saved : m));
  } catch (err) {
    setComments(c => c.map(m => m.id === tempId ? { ...m, error: true } : m));
  }
}

Локальная очередь при отсутствии сети

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

const queue = JSON.parse(localStorage.getItem('queue') || '[]');

function enqueue(action) {
  queue.push(action);
  localStorage.setItem('queue', JSON.stringify(queue));
  trySend();
}

async function trySend() {
  while (queue.length && navigator.onLine) {
    const action = queue[0];
    try {
      await fetch(action.url, action);
      queue.shift();
      localStorage.setItem('queue', JSON.stringify(queue));
    } catch {
      break; // stop until online
    }
  }
}

window.addEventListener('online', trySend);

Сравнение входящих данных в реальном времени

Если сервер отправляет весь список, сравните его с текущим состоянием и обновите только изменения — это позволяет избежать ненужных повторных отрисовок и сбоев анимации.

function applyServerSnapshot(newList, currentList) {
  const current = new Map(currentList.map(i => [i.id, i]));
  const result = newList.map(item => {
    const prev = current.get(item.id);
    if (!prev) return { ...item, _state: 'new' };
    if (prev.updatedAt !== item.updatedAt) return { ...item, _state: 'updated' };
    return prev;
  });
  return result;
}

Обновления на основе изменений

Это лучше полных снимков состояния: сервер отправляет только изменившиеся поля. Применяйте их постепенно — так используется меньше пропускной способности, а отрисовка выполняется быстрее.

// Server sends: { type: 'patch', id: 42, fields: { likes: 11 } }
ws.on('message', (msg) => {
  if (msg.type === 'patch') {
    setPosts(posts => posts.map(p =>
      p.id === msg.id ? { ...p, ...msg.fields } : p
    ));
  }
});

Ограничение частоты частых обновлений

Некоторые потоки отправляют множество обновлений в секунду (положение мыши, биржевые котировки). Ограничьте частоту обновлений интерфейса примерно до 60 Гц, чтобы браузер не начал работать рывками.

import { throttle } from 'lodash-es';

const applyUpdate = throttle((data) => {
  setData(data);
}, 16); // ~60fps

ws.on('tick', applyUpdate);

Индикаторы присутствия

Показывайте, кто ещё находится в сети или печатает, в реальном времени. Сервер публикует события присутствия, а клиент отображает список с аватарами.

ws.on('presence', ({ users }) => {
  setOnlineUsers(users);
});

ws.on('typing', ({ user, isTyping }) => {
  setTypingUsers(t => isTyping
    ? [...t.filter(u => u.id !== user.id), user]
    : t.filter(u => u.id !== user.id));
});

Баннер состояния соединения

Показывайте ненавязчивый баннер, когда соединение потеряно или восстанавливается. Скрывайте его после полного переподключения. Пользователи гораздо лучше переносят задержки, когда видят, что происходит.

{!isConnected && (
  <div className="connection-banner">
    Reconnecting... Your changes will sync when you're back online.
  </div>
)}

Разрешение конфликтов

Приложения, работающие в реальном времени, должны обрабатывать конфликтующие одновременные изменения. Стратегии: побеждает последнее изменение (просто, но с потерей данных), векторы версий, OT (операционное преобразование), CRDT (реплицируемые типы данных без конфликтов).

CRDT для совместной работы

CRDT (реплицируемые типы данных без конфликтов) позволяют редактировать документы совместно в реальном времени без центрального сервера. Библиотеки: Yjs, Automerge. Такой подход используют приложения наподобие Figma, Linear и Notion.

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

Потоки данных в реальном времени могут стать очень длинными. Используйте виртуализированные списки (react-virtuoso, @tanstack/react-virtual), чтобы отображать только видимые элементы — объём памяти и нагрузка на CPU не зависят от общего количества элементов.

Быстрая проверка

Что такое «оптимистическое обновление» в интерфейсе, работающем в реальном времени?

Повторение: шаблоны интерфейсов реального времени

Оптимистические обновления создают ощущение мгновенной работы приложения; при ошибке выполняйте откат. Для создаваемых элементов используйте временные ID. Для надёжной работы без сети — локальную очередь. Сравнивайте обновления сервера или применяйте изменения, чтобы избежать повторных отрисовок. Ограничивайте частоту потоков с высокой частотой обновлений. Показывайте присутствие (кто в сети и кто печатает) и баннеры состояния соединения. Используйте CRDT (Yjs, Automerge) для совместного редактирования. Виртуализируйте длинные списки, обновляемые в реальном времени.

Часто задаваемые вопросы

Урок «Шаблоны интерфейсов реального времени» бесплатный?

Да — полный текст урока «Шаблоны интерфейсов реального времени» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Frontend Academy, подпишись на CoddyKit PRO. Курс Frontend Academy содержит 4 уроков всего.

Чему я научусь в уроке «Шаблоны интерфейсов реального времени»?

Применяйте оптимистичные обновления для мгновенной обратной связи, используйте локальную очередь в автономном режиме и сравнивайте входящие данные, обновляя в интерфейсе только изменившиеся элементы Ты практикуешь Frontend Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Frontend Academy?

Предыдущий опыт не требуется. Frontend Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Шаблоны интерфейсов реального времени»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Frontend Academy?

Да. Каждый урок Frontend Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. API WebSocket: открытие, сообщение, закрытие и ошибка
  2. Интеграция клиента Socket.io
  3. Server-Sent Events для однонаправленной потоковой передачи
  4. Шаблоны интерфейсов реального времени
← Назад к Frontend Academy