Анализ реактивного волокна

внешний интерфейс алгоритм JavaScript React.js
Анализ реактивного волокна

введение

Когда реакция вошла в поле зрения каждого,Virtual DOMКонцепция (VDOM) привлекательна.Перед работой с реальным DOM детали, которые необходимо обновить, получаются путем сравнения передней и задней части VDOM, а затем используется реальный DOM, что снижает стоимость браузер, работающий с DOM несколько раз. Этот процесс, официально именуемый примирением, можно перевести как协调算法. Однако с развитием React сегодня, с увеличением масштабов фронтенд-приложений, согласование становилось все более исчерпанным, и React Fiber вышел как того требует время. React Fiber — это переработанный основной алгоритм React, над которым работала команда React в течение двух лет.

мотивация

ВДОМ, которому тогда все аплодировали, почему сегодня немного подустал, приходится исходить из принципа его работы. Когда React был выпущен, предполагалось, что будущий рендеринг пользовательского интерфейса будет асинхронным, начиная сsetState()Это видно по дизайну React и механизму транзакций внутри react. До react@16, согласователь (теперь он называется согласователем стека) использовал рекурсию сверху вниз, начиная с корневого компонента илиsetState()После запуска компонента обновляется все поддерево. Если дерево компонентов небольшое, проблем не будет, но когда дерево компонентов становится все больше и больше, стоимость рекурсивного обхода будет выше, а основной поток будет постоянно занят, так что периодические задачи, такие как компоновка, анимация, а интерактивные ответы на основном потоке не могут выполняться сразу, они обрабатываются, в результате чего возникает визуальный эффект Дуньки.

Теоретически максимальное количество кадров, которое может распознать человеческий глаз, составляет не более 30 кадров, а количество кадров в фильмах в основном зафиксировано на уровне 24. Оптимальная частота кадров браузера — 60, то есть рендеринг один раз в около 16,5 мс. Нормальный рабочий процесс браузера должен быть таким: операция -> рендеринг -> операция -> рендеринг -> операция -> рендеринг…

img

Но когда время выполнения JS слишком велико, оно становится таким, и FPS (отображаемые кадры в секунду) падает, вызывая визуальные фризы.

img

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

img

Принцип работы

Разделение задач синхронизации понятно каждому, но перед разделением мы сталкиваемся со следующими проблемами:

  • Снести что?
  • Как демонтировать?
  • Каков порядок выполнения после разделения?

что снести

# React@15
DOM 真实DOM节点
-------
Instances React 维护的 VDOM tree node
-------
Elements 描述 UI 长什么样子(type, props)

В react@15 обновление в основном выполняется в два этапа: 1. Фактическая работа diff diff заключается в сравнении состояния prevInstance и nextInstance, чтобы выяснить разницу и соответствующее изменение VDOM. diff — это, по сути, некоторое вычисление (обход, сравнение), и его можно разделить (половина вычисления, а затем вычисление позже). 2. Патч обновляет очередь разностей, рассчитанную алгоритмом diff, на реальный DOM-узел. React не вычисляет разницу и не выполняет исправление, а вычисляет все различия и помещает их в очередь различий, а затем одновременно выполняет метод исправления, чтобы завершить реальное обновление DOM.

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

img

как разобрать

Это приводит к решению React Fiber, которое разбивается на единицы одного волокна.Дерево волокон строится на основе дерева VDOM.Структура дерева точно такая же, но содержащаяся информация отличается. Ниже приведена частичная структура узла дерева волокон:

{
    alternate: Fiber|null, // 在fiber更新时克隆出的镜像fiber,对fiber的修改会标记在这个fiber上
    nextEffect: Fiber | null, // 单链表结构,方便遍历 Fiber Tree 上有副作用的节点
    pendingWorkPriority: PriorityLevel, // 标记子树上待更新任务的优先级

	stateNode: any, // 管理 instance 自身的特性
    return: Fiber|null, // 指向 Fiber Tree 中的父节点
    child: Fiber|null, // 指向第一个子节点
    sibling: Fiber|null, // 指向兄弟节点
}

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

img

исполнительный лист

Stack выполняется в блоке дерева, Fiber выполняется в блоке Fiber. Стек может выполняться только синхронно; Файбер может планировать обработку для Файбера. То есть, если предположить, что существует волокно, чья структура односвязного списка (связного списка) представляет собой A → B → C, когда A выполняется до тех пор, пока B не будет прерван, B → C может быть выполнен снова позже, что предназначено для обработки синхронизации. структуру стека сделать сложно.

Процесс выполнения React Fiber в основном делится на две фазы:

  1. render / reconciliation (interruptible)
  2. commit (not interruptible)

Основная работа первого этапа заключается в построении полного дерева волокон сверху донизу.В процессе повторного рендеринга по ранее сгенерированному дереву строится дерево волокон с именем workInProgress для операций обновления.

Предположим, что у меня есть структура DOM, показанная на рисунке выше, для рендеринга, а дерево волокон, показанное на рисунке ниже, будет сгенерировано во время первого рендеринга:

img

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

requestIdleCallback вызовет задачу с низким приоритетом в периоды простоя, в то время как requestAnimationFrame вызовет задачу с высоким приоритетом в следующем кадре стека, гарантируя, что основной поток выполняет модули волокна в соответствии с приоритетом. Порядок приоритета следующий: ввод текстового поля > задачи, которые необходимо выполнить в конце этого расписания > переход анимации > интерактивная обратная связь > обновление данных > задачи, которые не будут отображаться, но будут отображаться в будущем.

module.exports = {  
  // heigh level
  NoWork: 0, // No work is pending.
  SynchronousPriority: 1, // For controlled text inputs. 
  TaskPriority: 2, // Completes at the end of the current tick.
  AnimationPriority: 3, // Needs to complete before the next frame.
  
  // low level
  HighPriority: 4, // Interaction that needs to complete pretty soon to feel responsive.
  LowPriority: 5, // Data fetching, or result from updating stores.
  OffscreenPriority: 6, // Won't be visible but do the work in case it becomes visible.
};

В процессе возведения в квадрат реакция обнаруживает, что List, Item2 и Item3 изменились, сравнивая узлы волокна по очереди, и помещает тег в соответствующее сгенерированное дерево workInProgress Tree и проталкивает его в список эффектов.

img

img

Когда согласование заканчивается, все побочные эффекты, включая изменения DOM, записываются в список эффектов корневого узла, а операция обновления выполняется на втором этапе (commit), и этот процесс завершается.

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

Представьте себе будущее

  • Асинхронный рендеринг На конференции JS Conf, проходившей в Исландии в этом году, Дэн упомянул концепцию асинхронного рендеринга.Асинхронный рендеринг не означает загрузку компонентов один за другим, а означает, что при асинхронной загрузке он обеспечивает синхронный процесс. старые устройства , пожертвовав временем загрузки ради плавной работы. На самом деле в версии React@16 асинхронный рендеринг по умолчанию отключен, хотя его можно добиться путем взлома, но баги все равно будут, потому что тесты не пишутся.
  • Изменения жизненного цикла В версии react@16, хотя предыдущие функции жизненного цикла все еще поддерживаются, официальный представитель заявил, что некоторые из них будут отброшены в следующей версии Причина этого в основном вызвана согласованием согласования. В процессе рендеринга/согласования из-за концепции приоритета и кванта времени задача, скорее всего, будет заменена другими задачами с более высоким приоритетом на полпути выполнения или прекращена из-за нехватки времени. При повторном выполнении этой задачи она выполняется с нуля, что вызовет некоторыеwillЖизненные циклы могут вызываться несколько раз и влиять на производительность. Команда реагирования дала нам много времени, чтобы разобраться с этой проблемой, и официальная также предоставила много ссылоккейс, вы можете плавно перейти на следующую версию.

react@16 - это не столько водораздел, сколько переход. Большая работа проделана, чтобы вакцинировать пользователей и сказать вам, что делать дальше. react@17 станет тем, что вызовет бурю. Примирение примирения приносит слишком много возможностей в будущее реагирования, включая хуки, которые в самом разгаре недавно обсуждались сообществом, что на самом деле является возможностью, привнесенной Fiber. В последующих версиях лично я думаю, что будет много изменений в методе написания, в основном для повышения производительности сервисов, также есть оптимизация некоторых решений, созданных сообществом, чтобы сделать написание более удобным для пользователя (рефы и контекст в HOC).доставка), официальные решения частых проблем (асинхронная обработка данных) и т.д. Помимо преимуществ, конечно, есть и некоторые проблемы. С итерацией версий в React появляется все больше и больше концепций, и кривая обучения для новичков будет становиться все круче и круче.

Суммировать

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

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