Говоря о производительности интерфейса и преодолении узких мест приложения React

внешний интерфейс JavaScript браузер React.js
Говоря о производительности интерфейса и преодолении узких мест приложения React

React 状态管理与同构实战

Производительность всегда была очень важной темой во фронтенд-разработке. По мере того, как внешний интерфейс может делать все больше и больше, возможности браузера бесконечно расширяются и используются: от веб-игр до сложных одностраничных приложений, от сервисов NodeJS до веб-VR/AR, визуализации данных, фронтенд-инженеры всегда раздвигая границы. Некоторые из последовавших за этим проблем с производительностью были решены, а некоторые превратились в непреодолимую стену щита.

Так,Когда мы говорим о производительности, о чем именно мы говорим? Каковы характеристики производительности приложений, разработанных на основе фреймворка React?

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


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

С прошлого года я работаю с известными технологическими гигантамиЯн ХайцзинМы начали наш соавторский путь, и в этом году книга «React State Management and Isomorphic Practice», которую мы совместно полировали, наконец-то официально вышла в свет! В этой книге за основу взят стек технологий React.На основе введения в использование React анализируется идея Redux с уровня исходного кода, и в то же время основное внимание уделяется архитектурному шаблону рендеринга на стороне сервера. и изоморфные приложения. Книга содержит множество примеров проектов, которые не только открывают для пользователей дверь в стек технологий React, но и улучшают общее понимание читателем передовых областей.

Если вы заинтересованы в содержании книги или следующего содержания, пожалуйста, поддержите нас! Подробности в конце статьи, не уходите!


Ахиллесова пята проблем с производительностью

На самом деле существуют различные проблемы с производительностью: в процессе передачи по сети могут возникать узкие места, вызывающие задержки в рендеринге данных во внешнем интерфейсе, или в гибридных приложениях контейнер веб-просмотра накладывает ограничения. Но при анализе проблем с производительностью часто возникает концепция, от которой невозможно ускользнуть —Одиночный поток JavaScript.

** Браузер анализирует и отображает дерево DOM и дерево CSS, анализирует и выполняет JavaScript, и почти все операции выполняются в основном потоке. **Поскольку JavaScript может манипулировать DOM и влиять на рендеринг, поэтомуПоток движка JavaScript и поток пользовательского интерфейса являются взаимоисключающими.. другими словами,Выполнение кода JavaScript блокирует отображение страницы.

Узнайте из схемы ниже:

JavaScript 主线程

Несколько ключевых ролей на рисунке:

Call Stack: стек вызовов, в котором выполняется код JavaScript, соответствует движку V8 в Chrome и NodeJS. Когда он завершает выполнение всех текущих задач, стек пуст, ожидая получения задачи следующего тика в цикле событий.

Browser APIs: это мост между кодом JavaScript и внутренними компонентами браузера, так что код JavaScript может манипулировать DOM через API браузера, вызывать setTimeout, AJAX и т. д.

Event queue: каждый раз, когда через AJAX или setTimeout добавляется асинхронный обратный вызов, функция обратного вызова обычно добавляется в очередь событий.

Job queue: это канал с более высоким приоритетом, зарезервированный для промисов, что означает «выполнить этот код позже, но до следующего тика цикла событий». Он относится к спецификации ES, обратите внимание на различную обработку и не будет здесь расширяться.

Next Tick: указывает задачу, которую стек вызовов будет выполнять в следующем такте. Он состоит из обратного вызова в очереди событий, полной очереди заданий и некоторой или всей очереди рендеринга. Обратите внимание, что текущий тик войдет в следующий тик только тогда, когда очередь заданий пуста. Это включает в себя приоритет задач.Может быть, все больше знакомы с микрозадачами и макрозадачами, поэтому я не буду здесь подробно их раскрывать.

Event Loop: он будет «контролировать» (опрашивать), пуст ли стек вызовов, и когда стек вызовов пуст, задача в следующем такте будет помещена в стек вызовов циклом событий.

В основном потоке браузера, когда код JavaScript выполняется в стеке вызовов, он может вызывать API браузера для работы с DOM. Некоторые асинхронные задачи также могут выполняться: если эти асинхронные задачи обрабатываются как обратные вызовы, они часто добавляются в очередь событий; если они обрабатываются как промисы, они сначала помещаются в очередь заданий. Эти асинхронные задачи и задачи рендеринга будут обрабатываться и выполняться стеком вызовов в следующей последовательности.

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

Такое узкое место в производительности, какАхиллесова пятаТочно так же он в определенной степени ограничивает использование JavaScript.

Двустороннее противоядие от производительности

Как правило, у нас есть два решения для преодоления упомянутого выше узкого места:

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

Таким образом, эти подзадачи будут выполняться в разных циклах тиков стека вызовов, а основной поток может выполнять операции обновления пользовательского интерфейса между подзадачами.Представьте обычный сценарий: Если нам нужно отрендерить список, состоящий из 100 000 фрагментов данных, то вместо того, чтобы отрисовывать все данные сразу, мы можем сегментировать данные и использовать setTimeout API для их поэтапной обработки, а работа по построению списка отрисовки будет разделены на разные подзадачи, которые выполняются в браузере. Между этими подзадачами браузер может обрабатывать обновления пользовательского интерфейса.

  • Еще один инновационный подход: использованиеHTML5 Web worker

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

Анализ производительности React Framework

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

**Нативный JavaScript должен быть самым эффективным, с этим не поспоришь. **По сравнению с другими фреймворками React тратит больше времени на выполнение JavaScript, потому что:

Сборка виртуальной DOM -> вычислить разницу DOM -> сгенерировать патч рендеринга

вызвано этой серией сложных процессов. То есть в определенной степени:Знаменитая стратегия планирования React — согласование стека — узкое место производительности React.

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

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

Давайте рассмотрим типичный сценарий из статьи:Новый движок React — что такое React Fiber?

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

实例

В этом примере мы можем установить значение NUMBER_OF_BLOCK равным 100000. В это время нажмите кнопку, активируйте setState, и страница начнет обновляться. В этот момент щелкните поле ввода и введите несколько строк, таких как «привет, реагировать». можно увидеть:Страница не отвечает. После ожидания 7 секунд в поле ввода внезапно появилось ранее введенное «hireact». В то же время компонент BlockList также был обновлен.

Очевидно, что такой пользовательский опыт не является хорошим.

Производительность основного потока браузера в этих 7-ках показана на следующем рисунке:

performance 图示

Желтая часть — это время выполнения JavaScript, а также время, в течение которого React занимает основной поток; Фиолетовая часть — это время, когда браузер пересчитывает дерево DOM; Зеленая часть — это когда браузер рисует страницу.

Эти три задачи занимают в общей сложности 7 секунд в основном потоке браузера, и в это время браузер не может взаимодействовать с пользователем. Основная причина в том, что время выполнения желтой части больше и занимает 6 с, то есть React занимает основной поток на долгое время, из-за чего основной поток не может реагировать на пользовательский ввод. Это типичный пример.

Повышение производительности React — React Fiber

Основная команда React заранее предвидела существование рисков производительности и продолжала искать способы их устранения. Основываясь на поддержке браузером двух API, requestIdleCallback и requestAnimationFrame, команда React реализовала новую стратегию планирования — согласование Fiber.

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

Производительность основного потока браузера показана на следующем рисунке:

performance

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

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

React в сочетании с веб-воркерами

Концепция веб-воркеров не будет повторяться в этой статье, вы можете посетитьУзнать об адресах MDN. Сосредоточимся на мыслях:Если вы позволите React подключиться к веб-воркеру, где будет точка входа и как ее реализовать?

Как мы все знаем, стандартное приложение React состоит из двух частей:

  • Ядро React: отвечает за большую часть сложных вычислений Virtual DOM;

  • React-Dom: отвечает за взаимодействие с реальным DOM браузера для отображения контента.

Что ж, ответ прост: мы пытаемся запустить вычисления, связанные с React Virtual DOM, в веб-воркере. Основная часть React входит в потоки веб-воркеров.

Кто-то придумал такую ​​идею, пожалуйста, обратитесь к репозиторию ReactВыпуск №3092, что также привлекло внимание Дэна Абрамова. Хотя такое предложение было отклонено, это не помешало нам поэкспериментировать с React в сочетании с воркерами.

Talk is cheap, show me the code, and demo:Читатели могут получить доступздесь, веб-сайт реализует два приложения с собственным React и React с доступом к веб-воркерам и сравнивает их производительность. Что касается части кода, заинтересованные студенты могут написать мне лично.

окончательный вывод: только при изменении большого количества узлов веб-воркер будет иметь некоторое влияние на повышение производительности рендеринга. Когда количество узлов очень мало, производительность доступа к веб-воркерам может быть отрицательным преимуществом. Я думаю, это связано со стоимостью связи между рабочим потоком и основным потоком.

С этой точки зрения у версии React для веб-воркеров еще есть возможности для улучшения производительности, я кратко резюмирую это следующим образом:

  • Поскольку рабочий поток и основной поток используют для связи postMessage, затраты на производительность велики.Мы можем использовать идею пакетной обработки, чтобы уменьшить количество сообщений.

Не очень разумно вызывать postMessage для уведомления основного потока каждый раз, когда необходимо изменить DOM. Таким образом, идея пакетной обработки может быть использована для сбора содержимого DOM, подлежащего обновлению, вычисленного в рабочем потоке, и последующей его равномерной отправки. Таким образом, гранулярность пакетной обработки очень интересна. Если дойти до крайности, каждый батчинг собирает много изменений и задерживает отправку их в основной поток, то один батчинг будет давить на реальный процесс рендеринга браузера, но это будет контрпродуктивно.

  • При использовании postMessage для доставки сообщений используйте передаваемые объекты для загрузки данных.
  • О рабочей версии syntheticEvent

В Native React есть система событий, которая прослушивает все события браузера на верхнем уровне, затем преобразует их в синтетические события и доставляет их слушателям событий, которые мы определяем в Virtual DOM.

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

Еще многое предстоит узнать о React в сочетании с рабочими процессами., например: гарантия синхронизации preventDefault и stopPropogation при обработке событий (связь между рабочим потоком и основным потоком асинхронна); использование нескольких рабочих (более одного рабочего) для исследования и т. д. Если читателям будет интересно, я напишу об этом отдельную статью.

Redux и веб-воркеры

Поскольку React может получить доступ к веб-воркерам, конечно, Redux также может извлечь уроки из этой идеи.Чистый Redux в сложных вычислениях редьюсера в рабочем потоке — не лучшая идея?

Я использую "Проблема N-ферзейДля имитации крупномасштабных вычислений, помимо этого чрезвычайно трудоемкого алгоритма, на странице запущено несколько модулей для реализации логики рендеринга, которая часто обновляет DOM:

  • Модуль мигания, отображающий счетчик (с увеличением на 1 в секунду) каждые 16 мс в режиме реального времени;

  • Модуль счетчика, который обновляет цвет фона каждые 500 миллисекунд;

  • Модуль слайдера, который постоянно совершает возвратно-поступательные движения;

  • Переворот каждые 16 миллисекунд Модуль вращения на 5 градусов

Как показано на рисунке:

image.png

Эти модули регулярно и часто обновляют стили DOM для рендеринга. Обычно, когда основной поток JavaScript выполняет вычисления N-ферзей, эти процессы рендеринга останавливаются.

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

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

Так почему бы не выбрать промежуточное ПО и не использовать вместо него энхансеры? Идея дизайна общей библиотеки, принятая в этой демонстрации рабочего Redux, очень интересна Расширенный контент о волшебном Redux не будет расширяться Заинтересованные читатели могут найти соответствующий контент в моей недавно опубликованной книге. Пришло время рекламы. . .


Книга "React State Management and Isomorphism" написана мной и известным начальником фронтенд-технологий.Ян ХайцзинСовместные усилия сконденсировали наши накопления и опыт в процессе изучения и практики фреймворка React. **Помимо введения в структуру React, основное внимание уделяется анализу управления состоянием и рендеринга на стороне сервера изоморфных приложений. **В то же время он впитал в себя множество отличных идей от сообщества и провел индуктивное сравнение.

Эта книга написана Шэнь Доу, вице-президентом Baidu, Донг Руи, старшим фронтенд-инженером Baidu, Руаном Ифэном, известным экспертом по языку JavaScript, дядей Вольфом, евангелистом Node.js, justjavac, основателем из китайского сообщества Flarum, Сяоли, технический эксперт по интерфейсу в Sina Mobile и старший руководитель Baidu.Совместная рекомендация инженера по интерфейсу Гу Илина и многих других экспертов в круге интерфейса.

Заинтересованные читатели могутНажмите здесь, чтобы узнать подробности.Вы также можете отсканировать QR-код ниже, чтобы совершить покупку. Еще раз спасибо за вашу поддержку и поощрение! Призываю критиковать и исправлять!

React 状态管理与同构实战

React 状态管理与同构实战

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

Happy coding!