Продолжаем про React.
Если бы я сейчас пересобирал старую лекцию про React, я бы начал не с FPS.
Тогда я заходил через плавность интерфейса: пользователь нажал кнопку, ввёл текст, открыл попап — интерфейс должен ответить без подвисаний.
Это всё ещё правда. Но сейчас я бы быстрее переходил к другому вопросу: какую работу пользователь должен увидеть сразу, а какая может подождать.
Старый заход
В той лекции я шел через 60 FPS.
Идея простая: между двумя кадрами у браузера очень мало времени. Если JS надолго занял поток, браузер не успел отрисовать следующий кадр — интерфейс дёрнулся.
Это не устарело. Просто
60 FPS — слишком грубая рамка, если на ней остановиться.Fiber в этой истории был ответом на проблему: React перестал воспринимать рендер как один большой кусок работы. Новое дерево можно готовить частями, а не в режиме «начали — теперь все ждут».
Эта база держится. Если вы заблокировали главный поток тяжёлым JS, пользователю всё равно будет плохо.
Но как объяснение современного React этого уже мало.
Чего здесь не хватает
Проблема не только в том, сколько работы делает интерфейс. Проблема ещё и в том, какая это работа.
Пользователь печатает в инпуте — символ должен появиться сразу. Иначе это ощущается не как «рендер не успел», а как сломанная клавиатура.
А пересчитать результаты поиска, перестроить таблицу или подготовить следующий экран можно чуть позже. Пользователь не ждёт каждый промежуточный результат. Он ждёт, чтобы интерфейс не вставал колом.
Раньше мы почти всегда упирались в один совет: сделай работу быстрее. Профилируй, мемоизируй, выноси state ближе к месту использования.
И это всё ещё правильный совет. Просто он не закрывает весь кейс. Иногда работа нужная, но пользователь не обязан ждать её прямо сейчас.
Что изменилось в модели
Современный React всё меньше похож на прослойку для обновления DOM и всё больше — на механизм управления работой интерфейса.
Не только:
— что изменилось;
— какие компоненты надо пересчитать;
— какие DOM-изменения потом применить.
А ещё:
— что должно ответить сразу;
— что можно подготовить в фоне;
— что можно прервать и начать заново;
— что вообще лучше не тащить на клиент.
Вот почему старая рамка
60 FPS стала тесной.Пример такой фичи
startTransition — просто самый наглядный пример этой идеи.Ввод в поле — срочный. Пересчёт тяжёлого списка по этому вводу — часто нет. Поэтому одно обновление должно пройти сразу, а второе может догнать позже.
Но это не пост про
startTransition. Для него нужен отдельный разбор.Важно отметить, что transition не делает медленный код быстрым. Синхронный фильтр на десять тысяч строк React не превратит в фоновую магию.
Так что старые инструменты никуда не делись: убирать лишнюю работу всё ещё надо. Просто теперь появился ещё один вопрос: что из оставшейся работы можно показать позже.
Как я бы объяснял сейчас
Раньше я бы больше давил на то, как React сам организует рендер: строит новое дерево, может прервать подготовку, потом одним куском коммитит результат.
Сейчас я бы добавил второй слой: разработчик тоже всё чаще участвует в разговоре о приоритетах. Что должно ответить сразу. Что можно подготовить позже. Где показать старый экран, пока новый ещё собирается.
Разница в формулировке маленькая, а в голове большая.
Потому что дальше в эту же рамку ложатся транзишены, Suspense, стриминг и Server Components. Это не просто набор новых фич, а попытка разложить интерфейс по границам: что сделать сразу, что догрузить потом, что можно прервать.
Вот это, по-моему, и есть главный сдвиг.
Резюме
— ограничение по времени на кадр всё ещё актуально, но это не вся история;
— современный React удобнее объяснять через вопрос: что делать сейчас, а что позже;
— React и раньше управлял рендером, но теперь разработчик чаще явно участвует в выборе приоритетов;
— главный вопрос теперь не только «как сделать меньше работы», но и «что пользователь должен увидеть сразу».