Краткий обзор того, как работает React

внешний интерфейс алгоритм React.js Redux

Великие боги могут написать серию «глубоко и просто», а Сяобай напишет серию «правда, вкус и остановка», в основном для облегчения собственного понимания и закрепления.В конце концов, исходный код все равно будет большой каплей в начале, так что я готов понять это на простом вкусе. "Как работает React?"

Как работает Реакт? Знаете ли вы алгоритм Diff ---xx интервьюер

Путеводитель по яме от Xiaobai:

1. Приключения осеннего набора ВКонтакте (1)

2. Приключения осеннего набора ВКонтакте (2)

3. Приключения осеннего набора ВКонтакте (3)

4. Дополнения: Алгоритм предварительного собеседования и письменного тестирования

How React.js works

Virtual Dom VS Browser Dom

В дополнение к характеристикам среды MVC и страниц, управляемых данными, ядром React является то, что он «быстрый». Согласно распространенной поговорке: «Поскольку прямое манипулирование DOM приведет к перерисовке, перекомпоновке и т. д., что приведет к огромным потерям производительности и приведет к таким проблемам, как медленный рендеринг. React использует виртуальный DOM. Каждый раз, когда состояние обновляется, React сравнивает разница между виртуальным DOM. , а затем изменить измененный контент, и, наконец, React изменит реальный DOM, завершит обновление страницы и рендеринг».

Вышеприведенный абзац — это то, что мы все говорим, поэтому обычно здесь интервьюер спрашивает: «Что такое виртуальный DOM, как React сравнивается? Вы понимаете алгоритм Diff?». До этого было немного поломки, поэтому я решил попробовать:

  • Виртуальный DOM — это ядро ​​React, и его суть — объект JavaScript;
  • BrowserDOM (то есть реальный DOM страницы) — это объект Browser.

DOM нечего сказать, в основном говорить о некоторых характеристиках виртуального DOM:

  1. Суть — это JS-объект, представляющий настоящий DOM.
  2. Дом быстрее, чем реальное сравнение и эксплуатация мульти
  3. Создавайте 200 000 виртуальных узлов DOM в секунду
  4. Каждый раз, когда действие setState или отправляется, создается новый виртуальный дом.

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

Здесь всякий раз, когда генерируется виртуальный DOM, он распечатывается.Как видите, он представляет собой настоящий DOM, и каждый раз, когда создается новый, он также должен иметь возможность сравнить разницу между старым DOM и текущим. новый ДОМ.

Алгоритм сравнения

Как только что упоминалось, React будет захватывать содержимое каждого состояния, генерировать совершенно новый виртуальный DOM, а затем выяснять различия и различия, сравнивая его с предыдущим. Алгоритм React Diff имеет два соглашения:

  1. Два разных типа элементов, будут производить два разных дерева
  2. Разработчики могут использовать ключевое слово key, чтобы сообщить React, какие дочерние элементы стабильны и неизменны в DOM.

Второй момент, на который следует обратить внимание, например: например, под тегом ul реального DOM находится ряд<li>метка, однако, если вы хотите изменить метку, если вы дадите каждой меткеkeyValue, когда React сравнивает разницу, он может знать «вы все еще вы, но позиция изменилась». Помимо максимально быстрого поиска различий, React также хочет, чтобы изменения были минимальными. если добавленоkey, react сохранит экземпляр вместо того, чтобы создавать совершенно новый DOM, как раньше.

Чтобы быть более конкретным:

1234

После следующего состояния последовательность становится

1243

Для нас это фактически меняет порядок 4 и 3. Но как сообщить React, оригинальный3побежал к оригиналу4что позади? единственныйkeyработал.

Связанные вопросы интервью: Почему ключи должны быть добавлены в шаблоны списков в React

Пример операции сравнения

При сравнении Diff сначала сравнивает два корневых элемента.Когда разница заключается в изменении типа, может потребоваться больше «усилий».

Различные типы элементов дом

Например, текущее состояние имеет такое изменение:

<div>
    <Counter />
</div>

-----------
<span>
	<Counter />
</span>

Видно, что из<div>стал<span>, этот тип изменения приводит к прямому уничтожению старого дерева в целом, включая дочерние элементыCounter. так что старый экземплярCounterБудет полностью уничтожен, создайте новый экземпляр, очевидно, что эффективность низкая.

тот же тип элемента dom

Когда после сравнения обнаруживается, что они относятся к одному типу, с этим легко справиться. React проверит изменения своих свойств, а затем напрямую изменит свойства. Исходные экземпляры с гордостью сохраняются, что делает рендеринг эффективным, например так как:

<div className="before" title="stuff" />

<div className="after" title="stuff" />

КромеclassName,включатьstyleТо же самое верно, добавление, удаление и изменение не уничтожит все дерево dom, а изменит атрибуты, сохранив элементы и узлы под ним.

Составные элементы одного типа

Подобно вышеуказанному, для того же типа компонентного элемента, прочность детского элемента будет поддерживаться и не будет разрушена. Когда компонент обновляется, экземпляр сохраняется для того, чтобы поддерживать состояние между рендерами. Реагируйте обновления реквизит основного экземпляра компонента, чтобы соответствовать новым элементе и вызовам в основном экземпляреcomponentWillReceiveProps()а такжеcomponentWillUpdate().

Далее звонитеrender()метод, алгоритм diff повторяет предыдущий результат и новый результат

key props

если спередиkeyОбъяснение недостаточно понятное, вот реальный пример для иллюстрацииkeyважность.

  • Сценарий 1. Добавьте элемент в конец списка
<ul>
  <li>first</li>
  <li>second</li>
</ul>
------
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

Как видите, в этом случае React нужно толькоinsertДостаточно нового элемента и больше ничего менять не нужно, React в это время эффективен, но если во втором сценарии:

  • Сценарий 2. Вставка элемента вверху списка
<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>
---
<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

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

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>
------
<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

Таким образом, толькоkeyЗначение 2014 создано заново, а2015а также2016Просто переместил локацию.

Стратегия

Реагировать В чем разница между стратегией двух деревьев? Эта стратегия является основной частью:

Полный алгоритм сравнения двух деревьев — это задача O(n^3). Но во внешнем интерфейсе вы редко перемещаете элементы DOM между слоями. Таким образом, Virtual DOM будет сравнивать элементы только на одном уровне:

надdivтолько на том же уровнеdivНапротив, второй уровень будет сравниваться только со вторым уровнем. Таким образом, сложность алгоритма может достигать O(n).

обход в глубину

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

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

Например, узел № 1 p на приведенном выше рисунке изменился, поэтому он записывается следующим образом:

patches[1] = [{difference}, {difference}...]//用数组存储新旧节点的差异

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

В конце концов, пришло время поработать над настоящим DOM, применить различия, обновить и отрендерить.


Почему Redux нуждается в том, чтобы редукторы были чистыми функциями?

Это еще одна очень серьезная проблема, любой, кто использует Redux, знает, что редьюсеры получат предыдущий.stateа такжеactionв качестве параметра, затем возвращает новыйstate, этот новыйstateне в оригиналеstateМодификации на базе. Поэтому часто можно увидеть следующую надпись:

return Object.assign(...)
//或者----------
return {...state,xx:xxx}

Его роль состоит в том, чтобы вернуть совершенно новый объект.

Почему редюсеры должны быть чистыми функциями (возвращающие совершенно новый объект, не затрагивая исходный объект)?

чистая функция

По сути, чистые функции определяются следующим образом: не изменяют входные значения функции, полагаются на внешнее состояние (такое как базы данных, DOM и глобальные переменные) и имеют одинаковый результат для любого одного и того же входа.

Например, следующееaddФункция не изменяет переменные a или b и не зависит от внешнего состояния, всегда возвращая один и тот же результат для одних и тех же входных данных.

const add = (a,b) => {a + b};

Это чистая функция. Результат не влияет на a и b. Оглядываясь назад на редуктор, он отвечает всем характеристикам чистой функции, поэтому это чистая функция.

Почему это должна быть чистая функция?

Сначала скажу результат, если в редукторе, то в оригиналеstateВыполнение операции над ним и его возврат не приведет к повторному рендерингу React. Вообще ничего не изменится!

Далее посмотрите вниз на исходный код Redux:

Redux принимает заданное состояние (объект) и передает каждую часть состояния через цикл каждому соответствующему редьюсеру. Если что-то изменилось, редьюсер вернет новый объект. Если ничего не изменилось, редюсер вернет старое состояние.

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

Ну, то есть, с точки зрения исходного кода, редукс требует от разработчиков создания новыхstateэто совершенно новый объект. Так зачем так беспокоить разработчиков?

Взгляните на следующий пример: попробуйте сравнить, совпадают ли a и b.

var a = {
    name: 'jack',
    friend: ['sam','xiaoming','cunsi'],
    years: 12,
    ...//省略n项目
}
 
var b = {
    name: 'jack',
    friend: ['sam','xiaoming','cunsi'],
    years: 13,
    ...//省略n项目
}

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

имеют!

//接上面的例子
a === b  //false

Я не делаю глубокое сравнение, только поверхностное сравнение, эталонные значения не совпадают (не тот же объект), то есть не то же самое. ЭтоreduxизreducerПричина такой конструкции

использованная литература

1. Почему Redux нуждается в том, чтобы редукторы были чистыми функциями

2. Углубленный анализ: как реализовать алгоритм Virtual DOM

3.Learn how to code: how react.js works