Введение
Алгоритм diff доминирует в React и является гарантией производительности React V-dom и рендеринга, который также является самой очаровательной и привлекательной частью React.
Реагируйте на большую конструкцию - это идеальное сочетание разных и V-DOM, и эффективный алгоритм дифференцировки позволяет пользователям больше свободы обновлять страницу, чтобы разработчики могли также подальше от собственного эксплуатации DOM, более веселой линии и кода.
Но, как мы все знаем, сложность универсального diff вызовет серьезные проблемы с производительностью при большом количестве сравнений dom. Оптимизация diff командой React может позволить React выполнять рендеринг на стороне сервера. Какую оптимизацию выполняет diff React? В этой статье кратко обсудить!
Стратегия сравнения React
- Стратегия 1: игнорировать межуровневое перемещение узлов DOM в веб-интерфейсе;
- Стратегия 2: DOM-структуры, сгенерированные двумя компонентами одного типа, также похожи, а DOM-структуры, сгенерированные двумя компонентами разных типов, не совсем одинаковы.
- Стратегия 3: Для группы дочерних узлов на одном уровне различайте их, назначая уникальный и уникальный идентификатор (значение ключа) В сценарии веб-интерфейса, основываясь на трех вышеперечисленных пунктах, React оптимизирует различия деревьев, различий компонентов и различий элементов, уменьшая сложность универсального сравнения до порядка величины, обеспечивая производительность построения всего интерфейса пользовательского интерфейса!
три оптимизации
tree diff
Основываясь на стратегии 1, подход React состоит в том, чтобы разделить дерево dom на слои.Для двух деревьев dom сравниваются только узлы одного уровня, игнорируя межуровневое перемещение узлов в Dom, и сравниваются только все дочерние узлы под одним и тем же уровнем. родительский узел... Если сравнение обнаружит, что родительский узел не существует, все дочерние узлы под узлом будут удалены напрямую, и дальнейшее сравнение выполняться не будет, так что для завершения сравнения двух деревьев требуется только один обход дерева dom. .
== Итак, как быть с межуровневыми операциями dom? == Следующее объясняется легендой
Сравнивая два дерева, новое дерево справа обнаруживает, что узел A исчез, он напрямую уничтожит A и следующие дочерние узлы B и C; если на узле D будет найден дополнительный узел A, будет создан новый узел A. повторно созданный узел и соответствующие дочерние узлы.
Конкретная последовательность операций: создать A → создать B → создать C → удалить A.
Предложения по оптимизации
保证稳定dom结构有利于提升性能,不建议频繁真正的移除或者添加节点
component diff
Приложения React строятся на основе компонентов, а сравнение и оптимизация компонентов сосредоточены на следующих моментах:
1. Компоненты того же типа Следуйте дереву для сравнения деревьев V-Dom
2. Для различных типов компонентов сначала классифицируйте компонент в виде грязного компонента и замените все детские узлы под весь компонент
3. Виртуальный дом того же типа компонента не изменился. Реагирование позволяет разработчикам использовать необходимость использования CONCOMPONENTUPDATE (), чтобы определить, отличается ли компонентом. Правильное приложение может сохранить время расчета diff и улучшить производительность.
Как показано на рисунке выше, когда компонент D → компонент G, diff оценивается как компоненты разных типов.Хотя их структуры похожи или даже одинаковы, diff по-прежнему не сравнивает две структуры и напрямую уничтожает D и его компоненты. дочерние узлы, а затем создать новый связанный G. Это, очевидно, повлияет на производительность.Хотя чиновник считает, что такая ситуация возникает редко, влияние этого явления на развитие очень велико.
Предложения по оптимизации
对于同一类型组件合理使用shouldComponentUpdate(),应该避免结构相同类型不同的组件
element diff
Для узлов элементов на одном уровне diff предоставляет следующие три операции с узлами:
1. Узел вставки INSERT_MARKUP: выполнить операцию вставки узла на совершенно новом узле.
2. Мобильный узел MOVE_EXISING: новый набор компонентов имеет тип в старом наборе компонентов, и элемент может быть обновлен, то есть компонент вызывает ReceiveComponent, тогда можно повторно использовать предыдущий DOM для выполнения операции перемещения DOM.
3. REMOVE_NODE удаляет узел: На данный момент есть две ситуации: новый компонент имеет тип в старом наборе компонента, но соответствующий элемент не может быть обновлен, а старый компонент отсутствует в новом наборе.В этих двух случаях , необходимо выполнить операцию удаления узла.
Важность разницы ключ-значение
Как правило, diff будет выполнять все сравнения при сравнении наборов [A, B, C, D] и [B, A, D, C], то есть сравнивать каждую позицию одну за другой и находить, что элементы, соответствующие каждой позиции, Затем удалите все старые коллекции и замените их новыми коллекциями, как показано на рисунке выше, но такие операции, очевидно, являются сложными, неэффективными и влияющими на производительность операциями в React, потому что все элементы в новых коллекциях могут быть повторно использованы без необходимо для удаления и повторного создания затрат производительности и памяти, и нужно только переместить позицию элемента. React разработал эффективную стратегию для этого явления: он позволяет разработчикам добавлять уникальные ключевые значения в одну и ту же группу дочерних узлов на одном уровне, чтобы различать их. Значение — это один маленький шаг в коде, один гигантский скачок в производительности или даже кардинальные изменения!
== Дело в том, как React управляет элементами через ключи? Почему это так эффективно? ==
Улучшения алгоритма:
React сначала пройдёт по новому набору for(name in nextChildren) и оценит, есть ли одинаковые узлы в двух сравниваемых наборах по значению ключа, то есть if(prevChild === nextChild), как двигаться, если true , здесь Раньше нужно было выполнять сравнение положения перемещаемого узла в новом и старом (child._mountIndex) множествах, если (child._mountIndex
Пример изображения ниже:
-
nextIndex = 0, lastIndex = 0, получить B из новой коллекции, найти такой же узел B в старой коллекции, в старой коллекции: B._mountIndex = 1, child._mountIndex false, не выполнять операцию перемещения, обновить lastIndex = Math.max(prevChild._mountIndex, lastIndex), prevChild._mountIndex === B._mountIndex ==> true, обновить позицию B в новой коллекции: prevChild._mountIndex = nextIndex, в новой коллекция: B._mountIndex = 0, nextIndex++, чтобы судить о следующем узле.
-
nextIndex = 1, lastIndex = 1, получить A из нового набора, найти такой же узел A в старом наборе, в старом наборе: A._mountIndex = 0, child._mountIndex true, переместить A в enqueueMove ( this, child._mountIndex, toIndex), toIndex — это позиция, в которую нужно переместить A, update lastIndex = Math.max(prevChild._mountIndex, lastIndex), обновление позиции A в новой коллекции prevChild._mountIndex = nextIndex, в новая коллекция Средний: A._mountIndex = 1, nextIndex++, чтобы судить о следующем узле.
-
nextIndex = 2, lastIndex = 1, получить D из новой коллекции, найти такой же узел D в старой коллекции, в старой коллекции: D._mountIndex = 3, child._mountIndex false, не выполнять операция перемещения, обновление lastIndex = Math.max(prevChild._mountIndex, lastIndex), prevChild._mountIndex === D._mountIndex ==> true, обновление позиции D в новом наборе: prevChild._mountIndex = nextIndex, в новом наборе: D._mountIndex = 2, nextIndex++, чтобы судить о следующем узле.
-
nextIndex = 3, lastIndex = 3, получить C из нового набора, найти такой же узел C в старом наборе, в старом наборе: C._mountIndex = 2, child._mountIndex true, выполнить операцию перемещения on C enqueueMove(this, child._mountIndex, toIndex), toIndex — это позиция, куда C должен быть перемещен, update lastIndex = Math.max(prevChild._mountIndex, lastIndex), обновить позицию C в новой коллекции prevChild._mountIndex = nextIndex , в новой коллекции Middle: A._mountIndex = 3, nextIndex++, чтобы судить о следующем узле.
- Поскольку это последний узел, операция сравнения завершена.
== Итак, в дополнение к узлам многократного использования, когда в новую коллекцию добавлены новые узлы, как насчет узлов, которые необходимо удалить в старой коллекции? ==
Пример изображения ниже:
В этом случае React выполняет следующие шаги:
- nextIndex = 0, lastIndex = 0, получить B из новой коллекции, найти такой же узел B в старой коллекции, в старой коллекции: B._mountIndex = 1, child._mountIndex false, не выполнять операция перемещения, обновление lastIndex = 1, обновление позиции B в новом наборе, nextIndex++, чтобы судить о следующем узле.
- nextIndex = 1, lastIndex = 1, получить E из нового набора, такой же узел E не найден в старом наборе, nextIndex++ переходит к следующему узлу для оценки.
- nextIndex = 2, lastIndex = 1, получить C из новой коллекции, найти такой же узел C в старой коллекции, в старой коллекции: C._mountIndex = 2, child._mountIndex false, не перемещать C , обновить lastIndex = 2, обновить позицию C в новом наборе, nextIndex++, чтобы судить о следующем узле.
- nextIndex = 3, lastIndex = 2, получить A из нового набора, найти такой же узел A в старом наборе, в старом наборе: A._mountIndex = 0, child._mountIndex true, переместить A, обновить lastIndex = 2, обновить позицию A в новом наборе, nextIndex++ вводит оценку следующего узла.
- Когда сравнение различий между всеми узлами в новом наборе завершено, старый набор проходится, чтобы определить, есть ли в старом наборе узлы, которые не существуют в новом наборе.В это время обнаруживается, что узел D соответствует суждение, и операция удаления узла D выполняется, и операция diff завершается.
Недостаточный diff после оптимизации
В мире нет 100% идеального алгоритма, да и дифф в React тоже имеет свои недостатки, например, все элементы старой и новой коллекций можно использовать повторно, но последний элемент старой коллекции ставится на первую позицию в появится новая коллекция, и появится короткая доска. Пример изображения ниже:
В соответствии с приведенной выше оптимизацией последовательности позиция D в старом наборе является наибольшей, а наименьшей операцией является перемещение D в первую позицию. Фактически, операция diff переместит три узла перед D в соответствующую позицию. Обстоятельства могут повлиять на производительность рендеринга.
Предложения по оптимизации
在开发过程中,同层级的节点添加唯一key值可以极大提升性能,尽量减少将最后一个节点移动到列表首部的操作,当节点达到一定的数量以后或者操作过于频繁,在一定程度上会影响React的渲染性能。比如大量节点拖拽排序的问题。
Короче говоря, React предоставляет нам отличный алгоритм сравнения, который позволяет нам с удовольствием писать код в реальной разработке, но это не означает, что мы можем создавать наше приложение «по желанию». сильные и слабые стороны друг друга в конкретных сценариях и избегать некоторых недостатков алгоритма также способствуют повышению общей производительности приложения.
Использованная литература:
- «Подробный стек технологий React» Чен И — глава 3.5