Введение в конкурентный режим (экспериментально)

Внимание:

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

Большая часть информации на данной странице уже не актуальна и оставлена для истории. Актуальная информация приведена в посте блога React 18 Alpha announcement.

Перед выходом React 18 информация на этой странице будет обновлена.

На этой странице представлен теоретический обзор конкурентного режима. Больше практических моментов вы можете найти в следующих разделах:

Что такое конкурентный режим?

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

Эти функции пока являются экспериментальными и могут быть изменены. Они не являются частью стабильного выпуска React, но вы можете попробовать их в экспериментальной сборке.

Сравнение блокировки и прерываемого рендеринга

Чтобы объяснить конкурентный режим, мы будем использовать в качестве метафоры управление версиями. Если вы работаете в команде, вы, вероятно, используете систему управления версиями, такую как Git, и работаете с ветками. Когда ветка готова, нужно сделать слияние с основной веткой (main), чтобы коллеги смогли получить ваши изменения.

До того, как появился контроль версий, рабочий процесс разработки был совсем другим. Концепции веток не существовало. Если вы хотели отредактировать некоторые файлы, вы должны были попросить всех не трогать эти файлы, пока вы не закончите свою работу. Вы даже не могли начать работать над ними параллельно с этим человеком — вы были буквально заблокированы ими.

Это иллюстрирует, как UI-библиотеки, включая React, обычно работают сегодня. Как только они начинают рендерить обновление, включая создание новых узлов DOM и запуск кода внутри компонентов, они не могут прервать эту работу. Мы будем называть этот подход «блокирующим рендерингом».

В конкурентном режиме рендеринг не блокируется. Он прерывается. Это улучшает UX и открывает новые возможности. Прежде чем рассматривать конкретные примеры в следующих главах, мы познакомимся с новыми возможностями.

Прерываемый рендеринг

Представим список продуктов, который можно отфильтровать. Вы когда-нибудь печатали в фильтре списка и чувствовали, что он залипает при каждом нажатии клавиши? Избежать некоторых действий для обновления списка продуктов, скорее всего, не удастся. Например, создание новых DOM-узлов или построение вёрстки браузером. Однако, когда и как мы выполняем эту работу играет большую роль.

Распространенный способ обойти залипание — не обрабатывать входные данные при каждом изменении (debounce). В таком случае мы обновляем список только после того, как пользователь перестает печатать. Однако может быть неприятно если пользовательский интерфейс не обновляется во время ввода текста. В качестве альтернативы мы могли бы «тормозить» (throttle) обработку данных и обновлять список с определенной максимальной частотой. Но тогда на маломощных устройствах всё равно останется залипание. Оба подхода создают неоптимальный пользовательский интерфейс.

Причина залипания проста: как только начинается рендер, его уже нельзя прервать. Таким образом, браузер не может обновить ввод текста сразу после нажатия клавиши. Независимо от того, насколько хорошо UI-библиотека (например, React) может выглядеть при тестах производительности, если она использует блокировку рендера, некоторые действия в ваших компонентах всегда будут вызывать залипания. И зачастую это не так просто исправить.

Конкурентный режим устраняет это фундаментальное ограничение, делая рендеринг прерываемым. Это означает, что когда пользователь нажимает другую клавишу, React не нужно блокировать браузер от обновления ввода текста. Вместо этого он может позволить браузеру отрисовать обновление для входных данных, а затем продолжить визуализацию обновленного списка в памяти. Когда рендеринг завершен, React обновляет DOM, и изменения отражаются на экране.

Концептуально это можно считать реакцией на подготовку каждого обновления «в ветке». Точно так же, как вы можете отказаться от работы в ветках или переключаться между ними, React в конкурентном режиме может прервать текущее обновление, чтобы сделать что-то более важное, а затем вернуться к тому, что он делал раньше. Этот метод также может напоминать вам о двойной буферизации в видеоиграх.

Возможности конкурентного режима уменьшают необходимость применять ожидание (debouncing) и торможение (throttling) в пользовательском интерфейсе. Поскольку рендеринг прерываем, React не нужно искусственно задерживать выполнение, чтобы избежать залипание. Он может начать визуализацию сразу же, но прервать эту работу при необходимости, чтобы сохранить отзывчивость приложения.

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

Мы уже говорили, что конкурентный режим похож на работу React «в ветке». Ветки полезны не только для краткосрочных исправлений, но и для долгосрочных задач. Иногда вы можете работать над задачей, но это может занять недели, прежде чем она будет в «достаточно хорошем состоянии», чтобы влиться в основную ветку. Эта сторона нашей метафоры управления версиями относится и к рендеру.

Представьте себе, что мы перемещаемся между двумя экранами в приложении. Иногда у нас может быть недостаточно загруженного кода и данных, чтобы показать пользователю «достаточно хорошее» состояние загрузки на новом экране. Переход к пустому экрану или большому спиннеру может быть неприятным. Однако часто бывает так, что получение необходимого кода и данных не занимает слишком много времени. Может было бы лучше, если React смог бы оставаться на старом экране немного дольше и «пропустить» «плохое состояние загрузки», прежде чем показывать новый экран?

Хоть это возможно сделать и сегодня, такое трудно организовать. В конкурентном режиме эта функция встроена. Сначала React начинает готовить новый экран в памяти — или, как говорится в нашей метафоре, «на другой ветке». Поэтому React может подождать до обновления DOM, чтобы загрузить больше контента. В конкурентном режиме мы можем сказать React, чтобы он продолжил показывать старый экран, полностью интерактивный, с встроенным индикатором загрузки. И когда новый экран будет готов, React может привести нас к нему.

Конкурентность

Давайте повторим два примера выше и посмотрим, как конкурентный режим объединяет их. В конкурентном режиме React может работать с несколькими обновлениями состояния одновременно — так же, как ветки позволяют различным членам команды работать независимо:

  • Для обновлений с привязкой к ЦПУ (таких как создание узлов DOM и запуск кода компонента) конкурентность означает, что более срочное обновление может «прервать» уже начатую визуализацию.
  • Для обновлений с привязкой к вводу-выводу (таких как извлечение кода или данных из сети) конкурентность означает, что React может начать визуализацию в памяти ещё до того, как все данные поступят, и пропустить показ раздражающих пустых состояний загрузки.

Важно отметить, что способ использования React остаётся таким же. Такие понятия, как компоненты, пропсы и состояние, в принципе работают одинаково. Когда вы хотите обновить страницу, вы устанавливаете состояние.

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

Опыт внедрения в продакшене

Существует общая тема вокруг возможностей конкурентного режима. Его миссия заключается в том, чтобы помочь интегрировать результаты исследований взаимодействия человека и компьютера в реальный UI.

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

Точно так же мы знаем из исследований, что такие взаимодействия, как наведение курсора и ввод текста, должны быть обработаны в течение очень короткого периода времени, в то время как клики и переходы по страницам могут подождать немного дольше, не чувствуя задержки. Различные «приоритеты», которые использует конкурентный режим, внутренне примерно соответствуют категориям взаимодействия в исследовании человеческого восприятия.

Разработчики, уделяющие особое внимание UX, иногда решают подобные проблемы с помощью одноразовых решений. Однако эти решения редко сохраняются в течение длительного времени, поскольку их трудно поддерживать. В конкурентном режиме наша цель состоит в том, чтобы превратить результаты исследований пользовательского интерфейса в саму абстракцию и обеспечить идиоматические способы их использования. Как UI-библиотека, React хорошо подходит для этого.

Следующие шаги

Теперь вы знаете, что такое конкурентный режим!

Страницы, на которых можно узнать больше деталей по конкретным темам: