Анализ исходного кода React: процесс рендеринга (1)

внешний интерфейс React.js

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

Информация, связанная со статьей

пожалуйста, откройте сейчасмой кодИ найдите файл ReactDOM.js в src в папке react-dom. Сегодняшний контент начнется здесь.

render

Предположительно все писали подобный код при написании проектов React.

ReactDOM.render(<APP />, document.getElementById('root')

Этот код сообщает приложению React, что мы хотим отобразить компонент в контейнере, который обычно является кодом входа приложения React.renderПроцесс, и будет разделен на несколько статей, чтобы объяснить, потому что процесс слишком длинный.

Прежде всего, найдите строку 702 файла ReactDOM.js, чтобы начать сегодняшнее путешествие.

Эта часть кода на самом деле нечего сказать, только предостережениеlegacyRenderSubtreeIntoContainerЧетвертый параметр прописан мертвым в функцииforceHydrateдляfalse. Этот параметрtrueЭто указывает на то, что это рендеринг на стороне сервера, потому что мы анализируем рендеринг на стороне клиента, поэтому содержание этой части не будет расширяться в дальнейшем.

Введите следующийlegacyRenderSubtreeIntoContainerВ функции эта часть кода разделена на две части. первая часть нетrootПрежде чем нам сначала нужно создатьroot(соответствует этой статье), вторая часть состоит в том, что естьrootПроцесс рендеринга после этого (соответствует следующей статье).

Когда вы впервые вошли в функцию, не должно бытьroot, поэтому нам нужно создатьroot, вы можете найти этоrootОбъект также смонтирован вcontainer._reactRootContainer, который является нашим DOM-контейнером. Если у вас есть проект React, вы можете увидеть это, набрав в консоли следующий кодrootобъект.

document.querySelector('#root')._reactRootContainer

каждый может видетьrootдаReactRootПостроенный конструктором, и есть_internalRootобъект, которому посвящена данная статьяfiberОбъект, давайте посмотрим на него дальше.

Прежде всего, как было сказано вышеforceHydrateКонтент, связанный с атрибутами, в любом случае вам не нужно беспокоиться об этой части.shouldHydrateдля уверенностиfalse.

Следующий шаг — удалить все узлы внутри контейнера, вообще говоря, мы пишем такой контейнер

<div id='root'></div>

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

Наконец, создайтеReactRootобъект и возврат. В дальнейшем мы увидим несколькоroot, он может быть немного круглым.

существуетReactRootОдин шаг выполняется внутри конструктора, который заключается в созданииFiberRootобъект и монтируется на_internalRootначальство.Как дерево DOM,fiberОн также построит древовидную структуру (каждый узел DOM должен соответствоватьfiberобъект),FiberRootэто всеfiberкорневой узел дерева, мы узнаем оfiberсвязанный контент. Здесь упоминается,fiberВолокно и оптоволокно — это две разные вещи: первое представляет собой структуру данных, а второе — новую архитектуру.

существуетcreateFiberRootВнутри функции создаются два соответственноroot,ОдинrootназываетсяFiberRoot,еще одинrootназываетсяRootFiber, и оба они ссылаются друг на друга.

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

дляFiberRootВ терминах объектов нам нужно понять только два свойства, которыеcontainerInfoа такжеcurrent.前者代表着容器信息,也就是我们的document.querySelector('#root'); последнее указывает наRootFiber.

дляRootFiberОбъект, свойства, которые мы должны понимать, немного больше

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  this.stateNode = null;
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.effectTag = NoEffect;
  this.alternate = null;
}

stateNodeЭто было упомянуто выше и не будет повторяться здесь.

return,child,siblingЭти три свойства важны, ониfiberОсновная структура данных дерева.fiberДерево на самом деле представляет собой древовидную структуру односвязного списка.returnа такжеchildСоответствует родительскому и дочернему узлам дерева соответственно, и имеется только один родительский узелchildУказывает на своего первого дочернего элемента, даже если у родителя есть несколько дочерних элементов. Так как же связаны несколько дочерних узлов? ответsibling, каждый дочерний узел имеетsiblingАтрибут указывает на следующий дочерний узел, каждый из которых имеетreturnАтрибут указывает на родительский узел. Это может немного сбивать с толку, давайте разберемся с этим по картинке.fiberструктура дерева.

const APP = () => (
    <div>
        <span></span>
        <span></span>
    </div>
)
ReactDom.render(<APP/>, document.querySelector('#root'))

Если нам нужно отрендерить вышеуказанные компоненты, то их соответствующиеfiberДерево должно выглядеть так

Как видно из рисунка, каждому компоненту или узлу DOM соответствуетfiberобъект. Кроме того, если у вас есть проект React, вы также можете ввести следующий код в консоли для просмотраfiberВся структура дерева.

// 对应着 FiberRoot
const fiber = document.querySelector('#root')._reactRootContainer._internalRoot

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

говорящийeffectTagПрежде давайте разберемся, что такоеeffectКороче говоря, некоторые операции на DOM, такие как дополнения, делеции и изменения, затемeffectTagЭто для записи всех эффектов, но эта запись реализована побитовой операцией,здесьдаeffectTagсвязанный бинарный контент.

Если мы хотим добавитьeffectЕсли да, то можете написатьeffectTag |= Update; если мы хотим удалитьeffectЕсли да, то можете написатьeffectTag &= ~Update.

Ну наконец тоalternateАтрибуты. На самом деле в приложении React обычно есть дваfiebrTrees, одно называется старым деревом, а другое называется деревом workInProgress. Первый соответствует отрендерившемуся DOM-дереву, а второй — обновляемому дереву волокон, а также удобен для восстановления после прерывания. Узлы двух деревьев ссылаются друг на друга, что удобно для совместного использования некоторых внутренних атрибутов и уменьшения накладных расходов памяти. В конце концов, я сказал, что каждый компонент или DOM будет соответствоватьfiberпредмет, состоящий из больших словfiberДерево также будет очень большим.Если создать два дерева с одинаковыми атрибутами, будет потеряно много памяти и производительности.

Когда обновление завершится, дерево workInProgress заменит старое дерево.Эта практика называется двойной буферизацией, которая также является практикой оптимизации производительности.Заинтересованные студенты могут найти информацию самостоятельно.

Суммировать

Вышеизложенное представляет собой полное содержание этой статьи и, наконец, обобщает содержание этой статьи с помощью блок-схемы.

наконец

Чтение исходного кода — очень утомительный процесс, но и польза от него огромна. Если у вас возникнут какие-либо вопросы в процессе чтения, вы можете связаться со мной в области комментариев.

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

Следующая статья связана с процессом рендеринга.

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