Управляемое чтение
React
виртуальныйDOM
иDiff
АлгоритмReact
Очень важные основные функции этой части исходного кода также очень сложны.Понимание принципов этой части знаний поможет вам более глубоко освоить.React
очень нужно.
Я хотел виртуализироватьDOM
иDiff
Выложите алгоритм в статью и напишите виртуальныйDOM
Было обнаружено, что эта статья очень длинная, поэтому эта статья анализируется толькоDOM
.
Эта статья начинается с исходного кода и анализирует виртуальныйDOM
Основной принцип рендеринга (первый рендеринг) иReact
Очки оптимизации производительности для него.
если быть честнымReact
Исходный код действительно трудно читать 😅, если эта статья помогла вам, пожалуйста, поставьте лайк 👍, чтобы поддержать ее.
Общие проблемы в разработке
- Почему нужно цитировать
React
- индивидуальные
React
Почему компоненты должны быть капитализированы -
React
как предотвратитьXSS
-
React
изDiff
Алгоритмы и прочееDiff
Чем отличаются алгоритмы -
key
существуетReact
роль в - Как написать высокую производительность
React
компоненты
Если у вас остались вопросы по вышеуказанным вопросам, значит, выReact
виртуальныйDOM
а такжеDiff
Принцип алгоритма еще отсутствует, тогда, пожалуйста, прочитайте эту статью, возьмите ее.
Во-первых, давайте посмотрим, что такое виртуальныйDOM
:
виртуальный DOM
в родномJavaScript
В программе мы непосредственноDOM
создавать и изменять, в то время какDOM
Элементы взаимодействуют с нашим приложением через события, которые мы слушаем.
иReact
сначала преобразует ваш код вJavaScript
объект, то этоJavaScript
Затем объекты превращаются в реальныеDOM
. этоJavaScript
объекты называются виртуальнымиDOM
.
Например, следующий абзацhtml
Код:
<div class="title">
<span>Hello ConardLi</span>
<ul>
<li>苹果</li>
<li>橘子</li>
</ul>
</div>
существуетReact
может храниться как таковойJS
Код:
const VitrualDom = {
type: 'div',
props: { class: 'title' },
children: [
{
type: 'span',
children: 'Hello ConardLi'
},
{
type: 'ul',
children: [
{ type: 'li', children: '苹果' },
{ type: 'li', children: '橘子' }
]
}
]
}
Когда нам нужно создать или обновить элементы,React
Сначала сделайте этоVitrualDom
объекты создаются и изменены, а затемVitrualDom
Рендеринг объекта в реальныйDOM
;
когда нам нужноDOM
При мониторинге событий сначалаVitrualDom
следить за событиями,VitrualDom
будет родной проксиDOM
события, на которые нужно реагировать.
Зачем использовать виртуальный DOM
React
Зачем использоватьVitrualDom
Как насчет этого плана?
Повышение эффективности разработки
использоватьJavaScript
, при написании приложения мы фокусируемся на том, как обновитьDOM
.
использоватьReact
, ты просто должен сказатьReact
в каком состоянии вы хотите видеть представление,React
затем черезVitrualDom
удостоверитьсяDOM
соответствует этому состоянию. Вам не нужно манипулировать свойствами, обрабатывать события,DOM
обновить,React
сделает все это за вас.
Это позволяет нам больше сосредоточиться на нашей бизнес-логике, а неDOM
Эксплуатация, это может значительно повысить эффективность нашего развития.
Об улучшении производительности
Во многих статьях говоритсяVitrualDom
Может улучшить производительность, это утверждение на самом деле очень однобоко.
Прямое управлениеDOM
Без сомнения, это очень требовательно к производительности. ноReact
использоватьVitrualDom
Также не обойтись без операцииDOM
из.
Если это первый рендер,VitrualDom
Нет никакого преимущества, даже если он выполняет больше вычислений и потребляет больше памяти.
VitrualDom
Преимущество в том, чтоReact
изDiff
Алгоритмы и стратегии пакетной обработки,React
Перед обновлением страницы способ обновления и рендеринга рассчитывается заранее.DOM
. По сути, этим процессом расчета мы непосредственно оперируемDOM
Можно также судить и осознавать это самим, но это обязательно отнимет много сил и времени, а то, что мы делаем сами, зачастую не так хорошо, какReact
В ПОРЯДКЕ. Итак, в этом процессеReact
Помогли нам "улучшить производительность".
Так что я больше склоняюсь к тому,VitrualDom
Помогает нам повысить эффективность разработки, помогает нам рассчитать, как эффективнее обновлять при повторном рендеринге, а неDOM
Эксплуатация быстрее.
Если у вас есть разные мнения по анализу этой части, пожалуйста, не стесняйтесь делать кирпич в области комментариев.
Кроссбраузерная совместимость
React
на основеVitrualDom
Я реализовал набор собственных механизмов событий, смоделировал процесс всплытия и захвата событий и применил такие методы, как прокси-сервер событий и пакетное обновление, чтобы сгладить проблему совместимости событий различных браузеров.
Кроссплатформенная совместимость
VitrualDom
заReact
带来了跨平台渲染的能力。 отReact Native
Например.React
в соответствии сVitrualDom
нарисовать соответствующую платформуui
Слои, но позы, нарисованные на разных платформах, разные.
Принцип реализации виртуального DOM
Если вы не хотите читать сложный исходный код или у вас недостаточно времени сейчас, вы можете пропустить эту главу и перейти сразу👇Принцип виртуального DOM и обзор функций
На приведенном выше рисунке мы продолжаем расширяться.Согласно процессу на рисунке мы будем анализировать виртуальныйDOM
принцип реализации.
JSX и createElement
мы реализуемReact
Вы можете выбрать два метода кодирования при использовании компонентов, первый — использоватьJSX
написать:
class Hello extends Component {
render() {
return <div>Hello ConardLi</div>;
}
}
Второй – использовать непосредственноReact.createElement
написать:
class Hello extends Component {
render() {
return React.createElement('div', null, `Hello ConardLi`);
}
}
На самом деле два вышеуказанных способа написания эквивалентны,JSX
Просто дляReact.createElement(component, props, ...children)
Синтаксический сахар, обеспечиваемый данным способом. То есть всеJSX
В конечном итоге код будет преобразован вReact.createElement(...)
,Babel
Помогли нам в процессе этого преобразования.
как показано нижеJSX
<div>
<img src="avatar.png" className="profile" />
<Hello />
</div>;
будетBabel
преобразовать в
React.createElement("div", null, React.createElement("img", {
src: "avatar.png",
className: "profile"
}), React.createElement(Hello, null));
Уведомление,babel
будет оцениваться во время компиляцииJSX
Первая буква компонента в, когда первая буква строчная, считается нативнойDOM
Этикетка,createElement
Первая переменная компилируется в строку, когда первая буква заглавная, считается пользовательским компонентом,createElement
Первая переменная компилируется как объект;
Кроме того, посколькуJSX
быть впереди времениBabel
компилируется, так чтоJSX
Невозможно динамически выбрать тип во время выполнения, например следующий код:
function Story(props) {
// Wrong! JSX type can't be an expression.
return <components[props.storyType] story={props.story} />;
}
Его нужно записать следующим образом:
function Story(props) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}
Итак, используйтеJSX
вам нужно установитьBabel
плагинbabel-plugin-transform-react-jsx
{
"plugins": [
["transform-react-jsx", {
"pragma": "React.createElement"
}]
]
}
Создать виртуальный DOM
Давайте посмотрим на виртуальныйDOM
истинный вид следующегоJSX
Код выводится на консоль:
<div className="title">
<span>Hello ConardLi</span>
<ul>
<li>苹果</li>
<li>橘子</li>
</ul>
</div>
Эта структура очень похожа на структуру, которую мы описали выше, тогдаReact
Как преобразовать наш код в эту структуру, давайте посмотримcreateElement
Конкретная реализация функции (исходный код в тексте упрощен).
createElement
Операция, выполняемая внутри функции, очень проста,props
После обработки с дочерними элементами вернитеReactElement
Объекты, давайте разберем их один за другим:
(1).Обработка реквизита:
- 1. Ставим специальные свойства
ref
,key
отconfig
извлечь и присвоить - 2. Поместите специальные свойства
self
,source
отconfig
извлечь и присвоить - 3. Выньте и назначьте другие атрибуты, кроме специальных атрибутов, для
props
В последующих статьях будет подробно описана роль этих специальных свойств.
(2) Получить дочерние элементы
- 1. Получить количество дочерних элементов - все параметры после второго параметра
- 2. Если есть только один дочерний элемент, назначьте его
props.children
- 3. Если есть несколько дочерних элементов, заполните дочерние элементы массивом и назначьте его
props.children
(3) Обработка реквизита по умолчанию
- статические свойства компонента
defaultProps
определено по умолчаниюprops
сделать задание
ReactElement
ReactElement
Объединить несколько переданных и возвращенных атрибутов.
-
type
: Тип элемента, который может быть собственным типом HTML (строка) или пользовательским компонентом (функция илиclass
) -
key
: Уникальный идентификатор компонента, используемый дляDiff
алгоритм, который будет подробно описан ниже -
ref
: используется для доступа к родномуdom
узел -
props
: передается компонентуprops
-
owner
: сейчас строитсяComponent
принадлежитComponent
?typeof
: свойство, которое мы не часто видим, оно назначается какREACT_ELEMENT_TYPE
:
var REACT_ELEMENT_TYPE =
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
0xeac7;
видимый,?typeof
ЯвляетсяSymbol
Переменная типа, которая предотвращаетXSS
.
Если на вашем сервере есть уязвимость, которая позволяет пользователям хранить произвольныеJSON
объект, а клиентский код ожидает строку, что может стать проблемой:
// JSON
let expectedTextButGotJSON = {
type: 'div',
props: {
dangerouslySetInnerHTML: {
__html: '/* put your exploit here */'
},
},
};
let message = { text: expectedTextButGotJSON };
<p>
{message.text}
</p>
JSON
нельзя хранить вSymbol
переменная типа.
ReactElement.isValidElement
функция для определенияReact
Является ли компонент действительным, ниже приводится его конкретная реализация.
ReactElement.isValidElement = function (object) {
return typeof object === 'object' && object !== null && object.?typeof === REACT_ELEMENT_TYPE;
};
видимыйReact
будет отображаться без?typeof
Идентификация и компоненты, не прошедшие проверку правил, отфильтровываются.
когда ваша среда не поддерживаетSymbol
час,?typeof
назначается как0xeac7
, почему,React
Разработчик дал ответ:
0xeac7
немного похожеReact
.
self
,source
Только в непроизводственных средах он будет добавлен к объекту.
-
self
Указывает, какой экземпляр компонента включен в данный момент. -
_source
Указывает файл, из которого исходит код отладки (fileName
) и строки кода (lineNumber
).
Преобразование виртуального DOM в реальный DOM
Выше мы разобрали преобразование кода в виртуальныйDOM
процесс, давайте посмотрим наReact
как виртуализироватьDOM
преобразовать в реальныйDOM
.
Логика этой части более сложная.Давайте сначала воспользуемся блок-схемой, чтобы разобраться во всем процессе.Весь процесс можно условно разделить на четыре шага:
Процесс 1: Начальная обработка параметров
написание нашегоReact
После компонента нам нужно вызватьReactDOM.render(element, container[, callback])
Визуализируйте компонент.
render
Фактический вызов внутри функции_renderSubtreeIntoContainer
, давайте посмотрим на его конкретную реализацию:
render: function (nextElement, container, callback) {
return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
},
- 1. Использовать текущий компонент
TopLevelWrapper
обернуть
TopLevelWrapper
Просто пустая оболочка, которая обеспечиваетrootID
свойства, а вrender
Компонент возвращается в функцию.
TopLevelWrapper.prototype.render = function () {
return this.props.child;
};
ReactDOM.render
Первый параметр функции может быть нативнымDOM
так же может бытьReact
компонент, обертывающий слойTopLevelWrapper
Они могут быть обработаны единообразно в последующих рендерингах, независимо от того, являются ли они нативными или нет.
- 2. Определите, был ли элемент визуализирован в корневом узле.Если он был визуализирован, определите, выполнять ли операцию обновления или удаления
- 3. Обращение
shouldReuseMarkup
переменная, указывающая, нужно ли перемаркировать элемент - 4. Вызов проходит в параметрах, обработанных выше
_renderNewRootComponent
, вызываемый после завершения рендерингаcallback
.
существует_renderNewRootComponent
вызыватьinstantiateReactComponent
Классифицируйте и оберните компоненты, которые мы передаем:
В зависимости от типа компонента,React
Следующие четыре категории компонентов создаются на основе исходных компонентов, компоненты классифицируются и визуализируются:
-
ReactDOMEmptyComponent
: пустой компонент -
ReactDOMTextComponent
:текст -
ReactDOMComponent
:РоднойDOM
-
ReactCompositeComponent
: настроитьReact
компоненты
Все они имеют следующие три метода:
-
construct
: используется для полученияReactElement
для инициализации. -
mountComponent
: используется для созданияReactElement
соответствующая реальностьDOM
илиDOMLazyTree
. -
unmountComponent
: удалитьDOM
Узел, отвязать событие.
В частности, как рендерить, мы анализируем в процессе 3.
Процесс 2: пакетная обработка, вызов транзакции
существует_renderNewRootComponent
используется вReactUpdates.batchedUpdates
перечислитьbatchedMountComponentIntoNode
Делайте пакетную обработку.
ReactUpdates.batchedUpdates(batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context);
существуетbatchedMountComponentIntoNode
в использованииtransaction.perform
перечислитьmountComponentIntoNode
Пусть он вызывается на основе механизма транзакций.
transaction.perform(mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context);
Касательно пакетных транзакций, анализ передо мноймеханизм выполнения setStateВступлений больше.
Процесс 3: Генерация html
существуетmountComponentIntoNode
вызов функцииReactReconciler.mountComponent
Создание нативногоDOM
узел.
mountComponent
Внутри четыре объекта, сгенерированные процессом 1, фактически называютсяmountComponent
метод. Первый взгляд наReactDOMComponent
:
- 1. Для спец.
DOM
Этикетка,props
для обработки. - 2. Создать в соответствии с типом ярлыка
DOM
узел. - 3. позвонить
_updateDOMProperties
будетprops
вставить вDOM
узел,_updateDOMProperties
также может использоваться дляprops Diff
, первый параметр - это последний визуализированныйprops
, второй параметр — текущийprops
, если первый параметр пустой, он создается впервые. - 4. Создайте
DOMLazyTree
возражать и звонить_createInitialChildren
Визуализируйте дочерние узлы на нем.
Так почему бы просто не создатьDOM
node вместо того, чтобы создатьDOMLazyTree
Шерстяная ткань? Давайте сначала посмотрим_createInitialChildren
Что вы наделали:
Определить текущий узелdangerouslySetInnerHTML
Атрибуты, являются ли дочерние узлы текстовыми, а другие узлы вызываются отдельноDOMLazyTree
изqueueHTML
,queueText
,queueChild
.
Его можно найти:DOMLazyTree
на самом деле является объектом-оболочкой,node
имущество хранит реальноеDOM
узел,children
,html
,text
Храните дочерние элементы, узлы html и текстовые узлы отдельно.
Он предоставляет несколько методов для вставки дочерних элементов,html
а также текстовые узлы, эти вставки являются условными, когдаenableLazy
собственностьtrue
когда эти дети,html
и текстовый узел будет вставлен вDOMLazyTree
объект, когда онfalse
будет вставлен в реальныйDOM
в узле.
var enableLazy = typeof document !== 'undefined' &&
typeof document.documentMode === 'number' ||
typeof navigator !== 'undefined' &&
typeof navigator.userAgent === 'string' &&
/\bEdge\/\d/.test(navigator.userAgent);
видимый:enableLazy
является переменной, текущий браузерIE
илиEdge
когдаtrue
.
существуетIE(8-11)
иEdge
В браузере вставка узлов без потомков один за другим намного эффективнее, чем вставка всего сериализованного полного дерева узлов.
такlazyTree
Основное решение состоит в том, чтобыIE(8-11)
иEdge
Эффективность вставки узлов в браузер будет анализироваться в следующем процессе 4: если текущийIE
илиEdge
, вам нужно вставить рекурсивноDOMLazyTree
Кэшированные дочерние узлы, другим браузерам нужно вставить текущий узел только один раз, потому что их дочерние узлы уже отрисованы, не беспокоясь о проблемах с эффективностью.
Давайте взглянемReactCompositeComponent
, так как кода много, код этого модуля здесь выкладывать не буду, а следующие шаги в основном выполняются внутри:
- иметь дело с
props
,contex
и другие переменные, вызовите конструктор для создания экземпляра компонента - Определите, является ли это компонентом без сохранения состояния, обработайте
state
- перечислить
performInitialMount
жизненный цикл, обработка дочерних узлов, получениеmarkup
. - перечислить
componentDidMount
жизненный цикл
существуетperformInitialMount
функция, первый вызовcomponentWillMount
жизненный цикл, из-за обычаевReact
Компонент не является настоящим DOM, поэтому функция дочернего узла вызывается в функцииmountComponent
. Это также рекурсивный процесс, когда все дочерние узлы отрисовываются, возвращаютсяmarkup
и позвониcomponentDidMount
.
Процесс 4: рендеринг html
существуетmountComponentIntoNode
Вызов в функции будет сгенерирован на предыдущем шагеmarkup
вставлятьcontainer
контейнер.
При первом рендере_mountImageIntoNode
очиститcontainer
вызывается после дочернего узлаDOMLazyTree.insertTreeBefore
:
определитьfragment
узел или<object>
Плагин:
-
Если это два вышеуказанных, сначала вызовите
insertTreeChildren
Визуализируйте дочерний узел этого узла в текущий узел, а затем вставьте визуализированный узел вhtml
-
Если это другой узел, сначала вставьте узел во вставку в
html
, затем позвонитеinsertTreeChildren
вставить дочерний узел вhtml
. -
Если не в настоящее время
IE
илиEdge
, вам не нужно рекурсивно вставлять дочерние узлы, достаточно один раз вставить только текущий узел.
- Суждение не
IE
илиbEdge
Времяreturn
- как
children
не нулевой, рекурсивныйinsertTreeBefore
вставить - визуализировать HTML-узел
- отображать текстовый узел
Собственный прокси-сервер событий DOM
о виртуальномDOM
Механизм события , я специально написал статью, если вам интересно, вы можете 👇[Подробно о реакции] Механизм событий React
Принцип виртуального DOM и обзор функций
Процесс рендеринга компонента React
-
использовать
React.createElement
илиJSX
написатьReact
компоненты, практически всеJSX
В конечном итоге код будет преобразован вReact.createElement(...)
,Babel
Помогли нам в процессе этого преобразования. -
createElement
функциональная параkey
иref
и т. д. специальныеprops
обработать и получитьdefaultProps
по умолчаниюprops
Назначьте назначения и обработайте входящие дочерние узлы и, наконец, создайтеReactElement
объект (так называемый виртуальныйDOM
). -
ReactDOM.render
будет генерировать виртуальныйDOM
Рендеринг в указанный контейнер, который принимает пакетную обработку, транзакции и другие механизмы и оптимизирует производительность определенных браузеров и, наконец, преобразует его в реальныйDOM
.
Состав виртуального DOM
которыйReactElement
element, наш компонент в конечном итоге будет представлен в следующей структуре:
-
type
: Тип элемента, который может быть собственным типом HTML (строка) или пользовательским компонентом (функция илиclass
) -
key
: Уникальный идентификатор компонента, используемый дляDiff
алгоритм, который будет подробно описан ниже -
ref
: используется для доступа к родномуdom
узел -
props
: передается компонентуprops
,chidren
даprops
Свойство, в котором хранятся дочерние узлы текущего компонента, которые могут быть массивом (несколько дочерних узлов) или объектом (только один дочерний узел) -
owner
: сейчас строитсяComponent
принадлежитComponent
-
self
: (нерабочая среда) указывает, какой экземпляр компонента в данный момент находится в -
_source
: (нерабочая среда) указывает файл, из которого исходит код отладки (fileName
) и строки кода (lineNumber
)
Предотвратить XSS
ReactElement
объект имеет другой?typeof
собственность, этоSymbol
переменная типаSymbol.for('react.element')
, когда среда не поддерживаетSymbol
час,?typeof
назначается как0xeac7
.
Эта переменная предотвращаетXSS
. Если на вашем сервере есть уязвимость, которая позволяет пользователям хранить произвольныеJSON
объект, а клиентский код ожидает строку, что может представлять опасность для вашего приложения.JSON
нельзя хранить вSymbol
переменная типа, аReact
будет отображаться без?typeof
Идентифицированные компоненты отфильтровываются.
Пакет и транзакция
React
рендеринг виртуальныйDOM
Пакетные и транзакционные механизмы применяются для повышения производительности рендеринга.
О пакетной обработке и механизме транзакций в моей предыдущей статье[Подробно реагировать] механизм выполнения setStateЕсть подробное введение.
Целевая оптимизация производительности
существуетIE(8-11)
иEdge
В браузере вставка узлов без потомков один за другим намного эффективнее, чем вставка всего сериализованного полного дерева узлов.
React
пройти черезlazyTree
,существуетIE(8-11)
иEdge
В других браузерах по очереди отображается один узел, а в других браузерах весьDOM
Структура строится, а затем вставляется в контейнер целиком.
И, при рендеринге узлов по отдельности,React
также считаетсяfragment
и другие специальные узлы, эти узлы не будут вставляться в рендеринг один за другим.
Механизм событий виртуального DOM
React
Реализован набор механизмов событий сам по себе, который связывает все виртуальныеDOM
события на карте в реалDOM
события и делегировать все событияdocument
Выше я смоделировал процесс всплытия и захвата событий, а также выполнил унифицированное распределение событий.
React
Самостоятельно сконструированный синтетический объект событияSyntheticEvent
, который представляет собой встроенную кросс-браузерную оболочку событий. Он имеет тот же интерфейс, что и собственные события браузера, в том числеstopPropagation()
иpreventDefault()
Подождите, они работают одинаково во всех браузерах. Это сглаживает проблемы совместимости событий в разных браузерах.
Вышеприведенное анализирует только виртуальныйDOM
Принцип и процесс первого рендеринга, естественно сюда не входит виртуальныйDOM
провестиDiff
Процесс мы подробно обсудим в следующей статье.
Что касается вопросов, поднятых в начале, мы ответим на них единообразно в следующей статье.
Рекомендуемое чтение
- [Подробно реагировать] механизм выполнения setState
- [Подробно о реакции] Механизм событий React
- [Углубленное реагирование] От Mixin до HOC и Hook
конец
Версия в исходном коде этой статьиReact
Версия 15, относительная16
В версии будут некоторые неточности, примерно16
Изменения версии будут проанализированы отдельно в последующих статьях.
Если в тексте есть какая-либо ошибка, исправьте ее в области комментариев, или если у вас есть хорошие предложения по оформлению и опыту чтения статьи, укажите ее в области комментариев, спасибо за чтение.
Хотите прочитать больше качественных статей, скачайте исходный файл интеллект-карты в статье, прочтите статьюdemo
Исходный код, следуй за мнойблог на гитхабе, твоя звезда✨, лайки и внимание - движущая сила моего постоянного творчества!
Рекомендую обратить внимание на мой паблик WeChat [code secret garden], каждый день выкладывать качественные статьи, будем общаться и расти вместе.