Это третья статья моего разбора исходного кода React, если вы не читали предыдущие статьи, обязательно сначала прочтите их.первая статьяНекоторые меры предосторожности, упомянутые в разделе, могут помочь вам лучше читать исходный код.
Информация, связанная со статьей
- Исходный код React 16.8.6 Китайские комментарии, эта ссылка является ядром статьи, конкретный код и количество строк кода в статье основаны на этом репозитории
- Разогрев
- процесс визуализации (1)
Ссылка на содержание этой статьипроцесс визуализации (1), конечно, нет ничего плохого в том, чтобы не прочитать предыдущую статью, потому что содержание не сильно связано.
пожалуйста, откройте сейчасмой кодИ найдите файл ReactDOM.js в src в папке react-dom. Сегодняшний контент начнется здесь.
ReactRoot.prototype.render
В предыдущей статье мы рассмотрели, когдаReactDom.render
При выполнении внутренний сначала определит, существует ли он ужеroot
, если нет, он создаст одинroot
. В сегодняшней статье мы узнаем, что существуетroot
что будет потом.
Сначала вы можете найти строку 592 кода.
Как видите, вышеприведенный код вызываетunbatchedUpdates
Функция, знания, связанные с этой функцией, на самом деле очень важны в React.
Все знают, сколькоsetState
Выполненные вместе, не запускают несколько рендеров React.
// 虽然 age 会变成 3,但不会触发 3 次渲染
this.setState({ age: 1 })
this.setState({ age: 2 })
this.setState({ age: 3 })
Это потому, что внутренне это три разаsetState
Оптимизация — это обновление, термин — BatchUpdate, и мы также можем увидеть, как внутреннее обновление обрабатывается в последующем контенте.
Для рута нет необходимости в пакетном обновлении, так что вот вызовunbatchedUpdates
функция, сообщающая внутренним компонентам, что пакетные обновления не требуются.
затем вunbatchedUpdates
Обратный вызов внутренне оценивает, существует ли онparentComponent
. На этом шаге можно предположить, что не будетparentComponent
, потому что мало кто будетroot
внешний плюсcontext
компоненты. не существуетparentComponent
будет выполнятьroot.render(children, callback)
,здесьrender
ОтноситсяReactRoot.prototype.render
.
существуетrender
Сначала мы удалим внутреннюю функциюroot
,здесьroot
Относится к FiberRoot, если вы хотите узнать о FiberRoot, вы можете прочитатьпредыдущий пост. затем создалReactWork
Экземпляр , нам не нужно вникать в этот контент, функция состоит в том, чтобы передать все входящие компоненты после того, как компонент будет отрисован или обновлен.ReactDom.render
Все функции обратного вызова выполняются один раз.
Далее мы смотримupdateContainer
Как внутри.
Начнем с FiberRootcurrent
Он берет свой объект волокна из его свойств, а затем вычисляет два раза. Эти два времени очень важны в React, поэтому их нужно изучить в отдельном разделе.
время
прежде всегоcurrentTime
,существуетrequestCurrentTime
Основная функция внутреннего времени вычисления функцииrecomputeCurrentRendererTime
.
function recomputeCurrentRendererTime() {
const currentTimeMs = now() - originalStartTimeMs;
currentRendererTime = msToExpirationTime(currentTimeMs);
}
now()
то естьperformance.now()
, если вы не знаете этот API, вы можете прочитать егоСвязанная документация,originalStartTimeMs
— это переменная, которая генерируется при инициализации приложения React, и значение такжеperformance.now()
, и это значение не будет изменено в дальнейшем. Затем, после вычитания этих двух значений, получается, сколько времени прошло с момента инициализации приложения React.
Затем нам нужно снова вычислить вычисленное значение по формуле, здесь| 0
Функция состоит в том, чтобы взять целое число, то есть11 / 10 | 0 = 1
Далее предположим какие-то значения переменных, и всем будет легче понять, если их подставить в формулу.
еслиoriginalStartTimeMs
для2500
, текущее время5000
, то расчетная разница равна2500
, то есть с момента инициализации приложения React прошло 2500 миллисекунд, и конечный результат, полученный по формуле, таков:
currentTime = 1073741822 - ((2500 / 10) | 0) = 1073741572
Далее идет расчетexpirationTime
,Это время связано с приоритетом, чем больше значение, тем выше приоритет. И синхронизация является наивысшим приоритетом, ее значение равно1073741823
, константа, которую мы видели ранееMAGIC_NUMBER_OFFSET
плюс один.
существуетcomputeExpirationForFiber
Есть много функций филиала, но основной расчет только три строки кода, а именно:
// 同步
expirationTime = Sync
// 交互事件,优先级较高
expirationTime = computeInteractiveExpiration(currentTime)
// 异步,优先级较低
expirationTime = computeAsyncExpiration(currentTime)
Далее будем анализироватьcomputeInteractiveExpiration
Как вычисляется время внутри функции, конечноcomputeAsyncExpiration
Точно так же вычисляется время, за исключением того, что заменяются две переменные.
Приведенные выше коды на самом деле являются формулами, и мы можем рассчитать результат, подставив конкретные значения.
time = 1073741822 - ((((1073741822 - 1073741572 + 15) / 10) | 0) + 1) * 10 = 1073741552
Также вceiling
в функции1 * bucketSizeMs / UNIT_SIZE
Это необходимо для сглаживания разницы во времени в течение определенного периода времени. Независимо от того, сколько задач необходимо выполнить в течение сглаженной разницы во времени, их время истечения одинаково. Это также оптимизация производительности и помогает регулировать поведение страницы рендеринга. .
В итоге мы вычислилиexpirationTime
Можно сделать вывод в другой раз:
export function expirationTimeToMs(expirationTime: ExpirationTime): number {
return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
}
Если взять ранее рассчитанноеexpirationTime
Подставив приведенный выше код, получим следующий результат:
(1073741822 - 1073741552) * 10 = 2700
Это время фактически совпадает с тем, что мы рассчитали выше.2500
Разница в миллисекундах близка. потому чтоexpirationTime
Относится к времени истечения задачи. React вычисляет крайний срок выполнения задачи в соответствии с приоритетом задачи и текущим временем. Пока это значение больше текущего времени, React всегда может отложить выполнение этой задачи, чтобы разрешить выполнение задач с более высоким приоритетом, но как только крайний срок задачи прошел, задача должна быть выполнена немедленно.
Эта часть содержания рассчитывалась, и это может показаться головной болью. Конечно, если вам это покажется хлопотным, просто помните, что время истечения задачи рассчитывается путем прибавления константы к текущему времени (разные задачи имеют разные приоритеты).
Кроме того, вы можете увидеть более интуитивно понятный и простой способ расчета времени истечения срока действия в следующем коде, но эта часть кода еще не использовалась.
scheduleRootUpdate
Когда мы рассчитаем время, оно будет называтьсяupdateContainerAtExpirationTime
, этой функции на самом деле нечего разбирать, мы напрямую вводимscheduleRootUpdate
функция в порядке.
Сначала мы создадим одинupdate
,этот объект иsetState
тесно связанный
// update 对象的内部属性
expirationTime: expirationTime,
tag: UpdateState,
// setState 的第一二个参数
payload: null,
callback: null,
// 用于在队列中找到下一个节点
next: null,
nextEffect: null,
дляupdate
Что касается свойств внутри объекта, нам нужно сосредоточиться на следующем:next
Атрибуты. потому чтоupdate
По сути, это узел в очереди, по этому атрибуту можно найти следующий.update
. Для пакетного обновления мы можем создать большеupdate
, поэтому нам нужно преобразовать этиupdate
Объединены и сохранены, при необходимости извлекаются для обновленийstate
.
существуетrender
Процесс на самом деле является операцией обновления, но мы неsetState
, так поставьpayload
назначить как{element}
.
Далее мы будемcallback
назначить наupdate
свойства, здесьcallback
ещеReactDom.render
третий параметр.
Тогда мы просто создадимupdate
объекты вставляются в очередь,enqueueUpdate
Внутренних веток у функции много и код простой.Код здесь больше не выкладывается.Кому интересно,могут прочитать сами. Основная функция функции состоит в том, чтобы создать или получить очередь, а затем поместитьupdate
объект поставлен в очередь.
последний звонокscheduleWork
Функция, вот контент, связанный с планированием, который мы подробно проанализируем в следующей статье.
Суммировать
Вышеизложенное является полным содержанием этой статьи.На самом деле ядро этой статьи по-прежнему касается времени расчета, потому что это время тесно связано с последующим планированием.Наконец, содержание двух статей о процессе рендеринга суммируется. через блок-схему.
наконец
Чтение исходного кода — очень утомительный процесс, но и польза от него огромна. Если у вас возникнут какие-либо вопросы в процессе чтения, вы можете связаться со мной в области комментариев.
Кроме того, написание этой серии является очень трудоемким проектом. Вам необходимо поддерживать комментарии к коду. Вы должны написать статью как можно больше, чтобы читатели могли ее понять. Наконец, вы должны добавить рисунки. Если вы считаете, что статья выглядит ладно, пожалуйста, не скупитесь на свои.
Следующая статья связана с процессом рендеринга.
Наконец, если вы считаете, что контент полезен, вы можете обратить внимание на мой официальный аккаунт «Внешняя часть действительно забавная», вас ждет много хорошего.