Переводчик: Кайт
Автор: Гетил Джордж Куриан.
Оригинальная ссылка:medium.com/@self-employed имеют два или…
Статья устарела, на основе реакции v16 скоро будет выпущена, эта статья только для справки
Я пытался понять глубоко и ясноVirtual-DOM
как это работает, и искал источники, которые более подробно объясняют детали того, как это работает.
Поскольку я не получил ни одной полезной информации в своих обширных поисках, я, наконец, решил изучитьreact
а такжеreact-dom
исходный код, чтобы лучше понять, как они работают.
Но прежде чем мы начнем, вы подумали о том, почему мы не рендерим напрямую
DOM
Обновить?
В следующем разделе я представлюDOM
как он был создан и почемуReact
создан с самого началаVirtual-DOM
DOM
как он был создан
(Изображение через Mozilla — https://developer.mozilla.org/en-US/docs/Introduction_to_Layout_in_Mozilla)
я не буду много говорить оDOM
создается и отображается на экране, но см.здесьа такжездесьпонять всеHTML
Перевести вDOM
и шаги для рисования на экране.
потому чтоDOM
представляет собой древовидную структуру, каждый разDOM
Когда некоторые частипереплавкашагов, а его потомки должны бытьперерисовать, поэтому, если в проекте нужно пройти больше узловПерекомпоновать/перерисовать, тем медленнее будет работать ваше приложение.
Что такое Virtual-DOM?Перекомпоновать/перерисоватьшаги для повышения производительности в больших и сложных проектах.
В следующем разделе будет рассказано больше оVirtual-DOM
Подробности о том, как это работает.
пониматьVirtual-DOM
Теперь, когда вы понимаетеDOM
Как он устроен, так что давайте узнаем об этом больше сейчасVirtual-DOM
Бар.
Здесь я сначала воспользуюсь небольшим приложением, чтобы объяснитьvirtual dom
Как это работает, чтобы вы могли легко увидеть, как это работает.
Я не буду вдаваться в подробности того, как работает первоначальный рендеринг, просто сосредоточусь на том, что происходит при повторном рендеринге, это поможет вам понять
virtual dom
а такжеdiff
Как работает алгоритм, как только вы понимаете процесс, понимание начального рендеринга становится простым :).
можно найти в этомgit repoНайдите исходный код этого приложения. Этот простой интерфейс калькулятора длинный:
КромеMain.js
а такжеCalculator.js
Кроме этого, другие файлы в этом репо можно игнорировать.
// Calculator.js
import React from "react"
import ReactDOM from "react-dom"
export default class Calculator extends React.Component{
constructor(props) {
super(props);
this.state = {output: ""};
}
render(){
let IntegerA,IntegerB,IntegerC;
return(
<div className="container">
<h2>using React</h2>
<div>Input 1:
<input type="text" placeholder="Input 1" ref="input1"></input>
</div>
<div>Input 2 :
<input type="text" placeholder="Input 2" ref="input2"></input>
</div>
<div>
<button id="add" onClick={ () => {
IntegerA = parseInt(ReactDOM.findDOMNode(this.refs.input1).value)
IntegerB = parseInt(ReactDOM.findDOMNode(this.refs.input2).value)
IntegerC = IntegerA+IntegerB
this.setState({output:IntegerC})
}
}>Add</button>
<button id="subtract" onClick={ () => {
IntegerA = parseInt(ReactDOM.findDOMNode(this.refs.input1).value)
IntegerB = parseInt(ReactDOM.findDOMNode(this.refs.input2).value)
IntegerC = IntegerA-IntegerB
this.setState({output:IntegerC})
}
}>Subtract</button>
</div>
<div>
<hr/>
<h2>Output: {this.state.output}</h2>
</div>
</div>
);
}
}
// Main.js
import React from "react";
import Calculator from "./Calculator"
export default class Layout extends React.Component{
render(){
return(
<div>
<h1>Basic Calculator</h1>
<Calculator/>
</div>
);
}
}
генерируется при начальной загрузкеDOM
Это выглядит так:
(DOM после первоначального рендеринга)
Вот структура приведенного выше дерева DOM, построенного React внутри:
Теперь добавьте два числа и нажмите кнопку «Добавить» для более глубокого понимания.
чтобы понятьDiff
как работает алгоритм иreconciliation
Как запланироватьvirtual-dom
к реальномуDOM
Да, в этом калькуляторе я ввожу 100 и 50 и нажимаю кнопку «Добавить», ожидая, что будет выведено 150:
输入1: 100
输入2: 50
输出: 150
Так что же происходит, когда вы нажимаете кнопку «Добавить»?
В нашем примере при нажатии кнопки «Добавить» мы устанавливаем состояние с выходным значением 150:
// Calculator.js
<button id="add" onClick={() => {
IntegerA = parseInt(ReactDOM.findDOMNode(this.refs.input1).value);
IntegerB = parseInt(ReactDOM.findDOMNode(this.refs.input2).value);
IntegerC = IntegerA+IntegerB;
this.setState({output:IntegerC});
}}>Add</button>
компонент тега
(Примечание: компоненты, которые будут изменены)
Во-первых, давайте разберемся с первым шагом, как маркируется компонент:
-
все
DOM
Слушатели событий завернуты вReact
В пользовательском прослушивателе событий, поэтому при нажатии кнопки «Добавить» это событие щелчка отправляется прослушивателю событий реакции, который выполняет анонимную функцию, которую вы видите в приведенном выше коде. -
В анонимной функции мы вызываем
this.setState
Метод получает новое значение состояния. -
это
setState()
Метод будет похож на следующие строки кода, помечающие компоненты по очереди.
// ReactUpdates.js - enqueueUpdate(component) function
dirtyComponents.push(component);
Вам интересно, почему react помечает не кнопку, а весь компонент? Ну, это потому, что ты использовал
this.setState()
звонитьsetState
метод, и это это относится к этомуCalculatorкомпоненты
- Итак, теперь нашCalculatorКомпонент помечен, посмотрим, что будет дальше.
Пройти жизненный цикл компонента
отлично! Теперь, когда компонент помечен, что происходит дальше? следующее обновлениеvirtual dom
, затем используйтеdiff
Алгоритм, чтобы сделатьreconciliation
и обновить настоящийDOM
Прежде чем мы перейдем к следующему шагу, важно ознакомиться с различиями в жизненном цикле компонентов.
Вот нашиCalculatorкомпонент вreact
выглядит как в:
Calculator Wrapper
Вот шаги для обновления этого компонента:
-
Это через
react
обновляется путем запуска пакетного обновления; -
При пакетном обновлении он проверяет, помечен ли компонент, а затем запускает обновление.
//ReactUpdates.js
var flushBatchedUpdates = function () {
while (dirtyComponents.length || asapEnqueued) {
if (dirtyComponents.length) {
var transaction = ReactUpdatesFlushTransaction.getPooled();
transaction.perform(runBatchedUpdates, null, transaction);
- Затем он проверяет, есть ли состояние ожидания, которое необходимо обновить, или проблема
forceUpdate
.
if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
В нашем примере вы можете увидетьthis._pendingStateQueue
в оболочке калькулятора с новым состоянием вывода
-
Во-первых, он проверяет, используем ли мы
componentWillReceiveProps()
, если использовать полученноеprops
возобновитьstate
. -
Следующий,
react
проверит, используем ли мы его в компонентеshouldComponentUpdate()
, если мы используем, мы можем проверить, нужен ли компонентуstate
илиprops
изменения и повторный рендеринг.
Используйте эту схему, если вы знаете, что вам не нужно повторно отображать компонент, тем самым повышая производительность.
- Следующие шаги по очереди
componentWillUpdate()
,render()
, Ну наконец тоcomponentDidUpdate()
Начиная с шагов 4, 5 и 6, мы используем только
render()
- Теперь давайте посмотрим глубже
render()
Что произошло за это время?
рендеринг
Virtual-DOM
Сравните различия и перестройте
Компоненты рендеринга — обновлениеVirtual-DOM
, бегатьdiff
алгоритм и обновление до реальногоDOM
середина
В нашем примере все элементы в этом компоненте будут вVirtual-DOM
перестроен в
Он проверяет, что смежные отображаемые элементы имеют одинаковый тип и ключ, а затем согласовывает компоненты, тип которых соответствует ключу.
var prevRenderedElement = this._renderedComponent._currentElement;
//Calculator.render() method is called and the element is build.
var nextRenderedElement = this._instance.render();
Важным моментом является то, что здесь находится вызывающий компонент
render
место метода. Например,Calculator.render()
этоreconciliation
Процесс обычно состоит из следующих шагов:
Метод рендеринга компонента — обновляет виртуальный DOM, запускает алгоритм сравнения и, наконец, обновляет DOM.
Красная пунктирная линия означает все
reconciliation
Шаги будут повторяться для следующего дочернего узла и дочерних узлов внутри дочернего узла.
Приведенная выше блок-схема суммируетVirtual DOM
как обновляется фактический DOM.
Я мог пропустить несколько шагов, сознательно или неосознанно, но эта таблица охватывает большинство ключевых шагов.
Так что вы можете увидеть это в нашем примереreconciliation
Как это работает:
я пропускаю предыдущий<div>
изreconciliation
, направлять вас, чтобы увидетьDOM
сталиOutput:150
шаги обновления,
-
Reconciliation
из класса этого компонента с именем "контейнер"<div>
Начинать - Его дочерний элемент - это вывод, содержащий
<div>
, следовательно,react
начнется с этого дочернего узлаreconciliation
- Теперь у этого дочернего узла есть дочерние узлы
<hr>
а также<h2>
- так
react
будет<hr>
воплощать в жизньreconciliation
- Далее он начнется с
<h2>
изreconciliation
запускается, так как имеет собственные дочерние узлы, выходной иstate
вывод, он начнет работать на обоихreconciliation
- Первый выходной текст проходит
reconciliation
, так как это ничего не меняет, поэтомуDOM
Ничего не нужно менять. - Далее, из
state
Выход проходит черезreconciliation
, так как теперь у нас есть новое значение, 150,react
обновит настоящийDOM
. ...
реальностьDOM
оказание
В нашем примере вreconciliation
За это время изменились только поля вывода, как показано ниже, а рисунок мерцает в консоли разработчика.
перерисовывать только вывод
и в реалеDOM
Обновлено дерево компонентов на
В заключение
Заключение Хотя этот пример очень прост, он даст вам базовое пониманиеreact
что происходит внутри.
Я не стал выбирать более сложное приложение, потому что рисовать все дерево компонентов очень утомительно. :-|
reconciliation
процессReact
- Сравните предыдущий внутренний экземпляр со следующим внутренним экземпляром
- обновить внутренний экземпляр
Virtual DOM
(JavaScript
объект) в структуре дерева компонентов. - Обновляйте только реальные изменения узлов и их дочерних элементов, которые имеют реальные изменения.
DOM
.
(Примечание:в авторскомreact
версияv15.4.1
)