В конце блок-схемы в разделе «Анализ исходного кода React — обновления компонентов и транзакции»:
Части в синих прямоугольниках — это основные коды алгоритма Diff.updateChildrenтак же какprocessUpdates, после получения очереди обновлений компонентов через алгоритм Diff, обновить ее за один раз.
Код алгоритма Diff (не волнуйтесь, основные шаги алгоритма будут подробно описаны ниже):
_updateChildren: function (nextNestedChildrenElements, transaction, context) {
var prevChildren = this._renderedChildren;
var removedNodes = {};
var nextChildren = this._reconcilerUpdateChildren(prevChildren, nextNestedChildrenElements, removedNodes, transaction, context);
if (!nextChildren && !prevChildren) {
return;
}
var updates = null;
var name;
var lastIndex = 0;
var nextIndex = 0;
var lastPlacedNode = null;
for (name in nextChildren) {
if (!nextChildren.hasOwnProperty(name)) {
continue;
}
var prevChild = prevChildren && prevChildren[name];
var nextChild = nextChildren[name];
if (prevChild === nextChild) {
updates = enqueue(updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex));
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
prevChild._mountIndex = nextIndex;
} else {
if (prevChild) {
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
}
updates = enqueue(updates, this._mountChildAtIndex(nextChild, lastPlacedNode, nextIndex, transaction, context));
}
nextIndex++;
lastPlacedNode = ReactReconciler.getNativeNode(nextChild);
}
for (name in removedNodes) {
if (removedNodes.hasOwnProperty(name)) {
updates = enqueue(updates, this._unmountChild(prevChildren[name], removedNodes[name]));
}
}
if (updates) {
processQueue(this, updates);
}
this._renderedChildren = nextChildren;
}
В книге «Into the React Stack» есть лучшее объяснение алгоритма Diff. На самом деле, просто запомните несколько принципов и моментов оптимизации алгоритма при расчете очереди обновлений.
Сложность традиционного алгоритма сравнения составляет O(n^3), если вы хотите узнать больше, вы можете перейти к "A Survey on Tree Edit Distance and Related Problems"
Такого рода сложность на практике вызовет взрыв.Хотя центральный процессор текущего компьютера очень мощный, страница не может быть такой своевольной~.
Подход React к этому заключается в предоставлении разумных предположений и методов для упрощения всего процесса сравнения.
- Сценарии перемещения узлов DOM по уровням редки и могут быть проигнорированы. (Разумно можно попытаться обеспечить стабильность структуры DOM за счет проектирования компонентов. При необходимости можно настроить отображение DOM средствами CSS, т.к. операции создания, удаления и перемещения DOM могут быть меньше или меньше.Каждый DOM-узел браузера — это большой объект со множеством методов и свойств.)
- Два компонента одного класса генерируют аналогичную структуру деревьев, два разных класса компонентов будут генерировать различную структуру дерева. (Разумно, существует повышенная воспроизводимость самих компонентов роль страницы, которая аналогична структуре и функции структуры страницы (или подобного дерева DOM) в компоненты абстрактного класса, это разумные абстрактные компоненты должны Встречайте эту статью предположим)
- Группу узлов на одном уровне можно выделить, установив уникальный ключ, чтобы дополнительно оптимизировать diff. (Это не предположение, а способ улучшить производительность)
- Для двух компонентов одного класса возможно отсутствие изменений в виртуальном доме. Поэтому React позволяет разработчикам использовать shouldComponentUpdate(), чтобы определить, нужно ли анализировать компонент с помощью алгоритма сравнения. (Разумно, собственное понимание разработчиком страницы может быть использовано для дальнейшей оптимизации diff. Конечно, это может быть связано с тем, что разработчик неправильно использует shouldComponentUpdate(), чтобы решить, нужно ли ее обновлять или нет, таким образом получая неверный результат. .. Но это странно сеи???, нечестно писать баг)
Основываясь на вышеизложенном, React выполняет иерархическое сравнение только в конкретном процессе Diff и сравнивает только узлы на одном уровне между старым и новым деревьями. Существует три типа операций над узлами: вставка, перемещение и удаление.
Процесс оценки операций перемещения узла со ссылкой на слова из «Подробного стека технологий React»:
Сначала пройдемся по узлам нового набора, по (имя в nextChildren), по уникальному ключу можно судить, есть ли одинаковые узлы в старом и новом наборах, если (prevChild === nextChild), если есть являются одинаковыми узлами, то выполняется операция перемещения, но перед перемещением необходимо сравнить положение текущего узла в старой коллекции с lastIndex.Если (child._mountIndex
должен знать об этом"Это последовательный метод оптимизации. LastIndex всегда обновляется, указывая, что посещенный узел находится в самой правой позиции (т. е. наибольшей позиции) в старой коллекции. Если текущий посещенный узел в новой коллекции больше, чем lastIndex, это означает, что текущий посещенный узел находится в состоянии. Если старый набор более поздний, чем предыдущий узел, узел не будет влиять на положение других узлов, поэтому его не нужно добавлять в очередь различий, то есть нет операции перемещения выполняется."Это предложение. Это означает, что если позиция узла в старом наборе уже находится позади последнего узла, который вы оценили ранее, то этот узел уже находится позади измененного узла, а предыдущий измененный узел находится в порядке. Порядок уже правильный, нет необходимости переместить, иначе узел необходимо переместить.
Еще одна вещь, которую нужно знать, это то, что если вы не присвоите значение ключу, React по умолчанию будет использовать значение индекса в процессе обхода. Значение индекса здесь относится к порядковому номеру обхода узла, и эффект эквивалентен некоторым небольшим партнерам, использующим индекс массива списка в качестве ключа.Это на самом деле нехорошо, потому что ключ узла имеет отношение к положению узла и самого узла, то есть если у меня в списке 10 узлов, то ключ от 1 до 10 в порядке обхода, а потом я в начале списка добавляется узел.В это время ключ устанавливается в соответствии с порядком обхода списка.Потом изменились ключи исходных 10 узлов, а ключи старого и новые узлы неправильно сопоставлены. Вы должны знать, что ключ находится в React для компонента. Идентификаторы, неправильные или повторяющиеся ключи приведут к ошибкам React ... поэтому....... ключ должен быть уникальным идентификатором, связанным с сам узел.
Пол О'Шаннесси, один из авторов реакции, упомянул:
Ключ на самом деле не о производительности, это больше об идентичности (что, в свою очередь, приводит к лучшей производительности). Случайно назначенные и изменяющиеся значения не формируют идентичность
Вы спросите, исходный код вышеописанного алгоритма diff не видит ключ, ну а по сути, ключ каждого компонента станет значением, соответствующим имени в объекте nextChildren&prevChildren, а ключ компонента shouldUpdateReactComponent в _reconcilerUpdateChildren также используется.
Для простого добавления и удаления узлов и сказано:
- Добавление нового узла заключается в создании нового узла и размещении его в пройденной позиции по порядку.
- Удаление узла заключается в обходе старой коллекции в цикле после завершения обхода уровня, чтобы определить, нет ли новой коллекции, и, если нет, удалить узел.
Разумеется, упомянутые выше операции перемещения, добавления и удаления узлов выполняются не сразу, а собираются в массив обновлений, а затем с помощью метода processUpdates за один раз обновляется конкретный DOM.
processUpdates: function (parentNode, updates) {
for (var k = 0; k < updates.length; k++) {
var update = updates[k];
switch (update.type) {
case ReactMultiChildUpdateTypes.INSERT_MARKUP:
insertLazyTreeChildAt(parentNode, update.content, getNodeAfter(parentNode, update.afterNode));
break;
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
moveChild(parentNode, update.fromNode, getNodeAfter(parentNode, update.afterNode));
break;
case ReactMultiChildUpdateTypes.SET_MARKUP:
setInnerHTML(parentNode, update.content);
break;
case ReactMultiChildUpdateTypes.TEXT_CONTENT:
setTextContent(parentNode, update.content);
break;
case ReactMultiChildUpdateTypes.REMOVE_NODE:
removeChild(parentNode, update.fromNode);
break;
}
}
}
Конкретная операция узла — это, например, работа узла конкретного DOM браузера.
function insertLazyTreeChildAt(parentNode, childTree, referenceNode) {
DOMLazyTree.insertTreeBefore(parentNode, childTree, referenceNode);
}
var insertTreeBefore = createMicrosoftUnsafeLocalFunction(function (parentNode, tree, referenceNode) {
if (tree.node.nodeType === 11) {
insertTreeChildren(tree);
parentNode.insertBefore(tree.node, referenceNode);
} else {
parentNode.insertBefore(tree.node, referenceNode);
insertTreeChildren(tree);
}
});
Node.insertBefore()Это API для манипулирования DOM в браузере.
Diff Хотите выполнить конкретный процесс, чтобы понять, что затем рекомендую одноступенчатую отладку, или посмотреть «углубленные технологии React Technology» в каштанах, я не буду рисовать рисовать ..... Устал ..... также Онлайн очень похожий поиск просто отлично.
В этой статье необходимо более подробно остановиться на некоторых особенностях использования ключа. [подлежит уточнению]
Использованная литература: