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

внешний интерфейс исходный код JavaScript React.js внешний фреймворк
Анализ исходного кода React (1): внедрение и монтирование компонентов

Когда мы сможем умело использовать React для фронтенд-разработки, мы неизбежно проявим большой интерес к внутреннему механизму React. Что такое компоненты? Это действительно ДОМ? На чем основано выполнение функции жизненного цикла?

В этой статье давайте сначала изучим реализацию и монтирование компонентов React.

1. Какие компоненты

Начните с написания самого простого компонента:

После написания приведенного выше кода мы получаем<A />Вот этот компонент, тогда давайте сначала разберемся<A />что. использоватьconsole.logРаспечатай:

Как можно видеть,<A />На самом деле это js-объект, а не настоящий DOM, на этот раз обратите вниманиеpropsявляется пустым объектом. Далее печатаем<A><div>这是A组件</div></A>, и посмотрите, что выводит консоль:

Мы видели,propsизменился из-за<A />Компонент вложен вdiv,divТекст снова вложен, поэтому в описании<A />объектpropsдобавлено вchildrenсвойство, значением которого является описаниеdivjs-объект. Точно так же, если мы выполняем многоуровневую вложенность компонентов, то фактически они находятся в родительском объекте.propsувеличить вchildrenПоля и соответствующие им значения описания, то есть многоуровневая вложенность js-объектов.

Приведенное выше описание основано на режиме разработки React ES6, фактически переданном в ES5.React.createClass({})Компоненты, созданные этим методом, точно такие же, как в ES6, и их также можно проверить, распечатав результаты компонентов в консоли, которые здесь повторяться не будут.

Так как же компоненты React выглядят как теги HTML, но на самом деле являются объектами?

Поскольку объявление нашего компонента основано наReactиComponent, так что сначала мы открываемReact.js, вы можете увидеть следующий код:

мы вimport React from 'react'При импорте объект React предоставляется в исходном коде. существуетextends Component, унаследовалComponentсвоего рода. Здесь необходимо отметить два момента:

  • Исходный код явно используетсяmodule.exportsвместоexport default, почему его можно успешно внедрить? На самом деле это заслуга парсера babel. это делает(ES6)import === (CommonJS)require. В то время как в машинописном тексте строгийexport defaultобъявление, поэтому его нельзя использовать в машинописном текстеimport React from 'react'Теперь заинтересованные читатели могут попробовать его.
  • мы можем написатьextends Componentтакже могу написатьextends React.Component, есть ли разница между ними? ответ отрицательный. так какComponentдаReact.Componentцитаты. то естьComponent === React.Component, который может быть записан в реальном проекте.

вместеReactComponentподсказки, которые мы открываемnode_modules/react/lib/ReactComponent.js:

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

Приведенный выше код показывает, что компонент A, который мы объявили в начале, на самом деле унаследован.ReactComponentПодкласс класса, прототип которогоsetStateи другие методы. Таким образом, компонент А уже имеет самый простой прототип.

резюме

2. Инициализация компонентов

После объявления A мы можем настроить методы внутри него или использовать методы жизненного цикла, такие какComponentDidMountПодождите, это точно так же, как когда мы писали «классы». Единственное отличие состоит в том, что класс компонента должен иметьrenderВывод метода примерно такой<div>这是A组件</div>Структура компонента, смонтированная на реальном DOM, может инициировать жизненный цикл компонента и стать частью дерева DOM. Сначала мы наблюдаем, как «классы» ES6 инициализируют реагирующий компонент.

Поместите исходный пример кода в babel:

в_Componentявляется объектомReactComponent,_inheritпутьextendsФункциональная реализация ключевых слов, это контент, связанный с ES6, мы пока его проигнорируем. Дело в том, что мы нашлиrenderметод на самом деле называетсяReact.createElementметод (фактически метод ReactElement). тогда мы открываемReactElement.js:

Увидев это, мы обнаруживаем, что на самом деле каждый компонентный объект проходит черезReact.createElementметод созданReactElementтип объекта. другими словами,ReactElment— это объект, который внутренне записывает характеристики компонентов и сообщает React, что вы хотите видеть на экране. существуетReactElementсередина:

параметр Функции
?typeof Идентификационная информация компонента
key Идентификация структуры DOM для повышения производительности обновления
props Информация о подконструкции (добавить, если есть)childrenполе/ничего не пусто) и свойства компонента (например,style)
ref Ссылка на настоящий DOM
_owner _owner === ReactCurrentOwner.current(ReactCurrentOwner.js), значением является объект для создания текущего компонента, значение по умолчанию равно null.

Прочитав вышеприведенный контент, я считаю, что у каждого есть определенное понимание сути компонентов React. выполнивReact.createElementсозданныйReactElementТип объекта js — «Компонент React», что точно соответствует результату, выведенному на консоль. Подводя итог, если мы пройдемclassключевое слово объявляет компоненты React, затем они анализируются в настоящий DOM, пока не будутReactElementjs объект типа.

резюме

Чтобы дополнить предыдущую интеллект-карту:

3. Монтаж компонентов

Мы знаем, что это можно сделатьReactDOM.render(component,mountNode)Смонтируйте пользовательский компонент/собственный DOM/строку в виде

Так как же реализован процесс монтажа?

ReactDOM.renderна самом деле вызывает внутреннийReactMount.render, а затем выполнитьReactMount._renderSubtreeIntoContainer. Из буквального смысла видно, что это логика вставки "дочернего DOM" в контейнер.Посмотрим на реализацию исходного кода:

Этот код очень важен,renderВсе функции функции здесь (нажмите на картинку для увеличения).

Давайте разберем входящие_renderSubtreeIntoContainerПараметры:

параметр Функции
parentComponent Родительский компонент текущего компонента при первом отображенииnull
nextElement Компонент для вставки в DOM, напримерhelloWorld
container контейнер для вставки, напримерdocument.getElementById('root')
callback Функция обратного вызова после завершения

Функции этих параметров хорошо понятны, далее проведем логический анализ построчно:

  • строка 2: добавить текущий компонент на предыдущий уровеньpropsпод свойствами. (Вложенные отношения родитель-потомок объясняются в начале этой статьи следующим образом:propsпоставка)
  • строка 4 ~ 22: вызовgetTopLevelWrapperInContainerметод, чтобы определить, есть ли компонент под текущим контейнером, обозначенный какprevComponent; если естьprevComponentзаtrue, выполнить процесс обновления, то есть вызвать_updateRootComponentметод. Если он не существует, удалите его. (перечислитьunmountComponentAtNodeметод)
  • Строка 24: обновляется ли он или выгружается, в конечном итоге он будет смонтирован в реальном DOM. посмотри._renderNewRootComponentИсходный код:

Проанализируйте процесс:

  • появляется строка 3instantiateReactComponentСпособ упаковки, об этом мы поговорим позже.
  • в строке 5batchedMountComponentIntoNodeвызов как транзакцияmountComponentIntoNode(Транзакция достанет статью для анализа), этот метод возвращает HTML, соответствующий компоненту, который записывается как переменная.markup. иmountComponentIntoNodeПоследний звонок_mountImageIntoNode, посмотрите исходный код:

Основной код — это последние две строки.setInnerHTMLэто метод, который будетmarkupУстановить какcontainerизinnerHTMLатрибут, который завершает вставку DOM.precacheNodeМетод заключается в сохранении обработанных объектов компонента в кэше для повышения скорости обновления структуры.

Процесс инициализации и монтирования компонента React здесь в основном ясен. существуетReactDOM.render()При использовании метода мы заметим, что этот метод может монтировать компоненты React, строки или собственный DOM. Теперь мы уже знаем, что на самом деле монтирование заключается в использованииinnerHTMLсвойства, но React обрабатывает их по-разному для разных структур элементов?

Как упоминалось выше, на предпоследнем этапе монтажа компонентов, который заключается в выполнении_renderNewRootComponentметод, мы видим, что существуетinstantiateReactComponentметод возвращает обработанный объект. ПосмотримinstantiateReactComponentИсходный код:

входящие параметрыnodeэтоReactDOM.renderКомпонентный параметр метода, входnodeи выводinstanceЕго можно свести в следующую таблицу:

node фактические параметры результат
null/false нулевой СоздайтеReactEmptyComponentкомпоненты
object && type === string виртуальный DOM СоздайтеReactDOMComponentкомпоненты
object && type !== string Реагировать компоненты СоздайтеReactCompositeComponentкомпоненты
string нить СоздайтеReactTextComponentкомпоненты
number номер СоздайтеReactTextComponentкомпоненты

Чтобы разобраться в процессе:

  • в соответствии сReactDOM.render()Когда передаются разные параметры, React создаст внутри четыре типа инкапсулированных компонентов, обозначенных какcomponentInstance.
  • а затем передать его в качестве параметраmountComponentIntoNodeВ методе получается соответствующий компоненту HTML, который записывается как переменнаяmarkup.
  • Преобразование реального свойства DOMinnerHTMLУстановить какmarkup, то есть вставка DOM завершена.

Итак, вопрос в том, как разобрать HTML на втором шаге выше? Ответ заключается в том, что в процессе инкапсуляции в четыре типа компонентов на первом этапе даются инкапсулированные компонентыmountComponetметод, выполнение этого метода запустит жизненный цикл компонента, тем самым проанализировав HTML.

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

4. Резюме

Используйте диаграмму, чтобы разобраться в процессе компонентов React от объявления до инициализации и монтирования: (нажмите, чтобы просмотреть увеличенное изображение)

обзор:
«Анализ исходного кода React (2): типы и жизненные циклы компонентов»
«Анализ исходного кода React (3): подробная транзакция и очередь обновлений»
«Анализ исходного кода React (4): система событий»
Контактный адрес электронной почты: sssyoki@foxmail.com