Виртуальный DOM — одно из самых больших преимуществ использования React. Однако, несмотря на то, что виртуальный DOM очень мощен, его также может быть немного трудно понять: это абстракция DOM, а поскольку DOM уже является абстракцией HTML-текста, виртуальный DOM, по сути, является абстракцией абстракции. . Так как же на самом деле работает виртуальный DOM и насколько он более эффективен, чем работа с самим DOM? Может быть полезно знать, как работает виртуальный DOM, чтобы разработать более эффективную программу, а также понять любые запутанные ошибки, которые могут возникнуть.

Во-первых, ДОМ.

Прежде чем обсуждать виртуальный DOM, было бы полезно вспомнить старый простой DOM или объектную модель документа. DOM является представлением HTML и включает интерфейс API, который позволяет нам находить и настраивать узлы с помощью встроенных методов, таких как getElementById и appendChild. Таким образом, DOM и его интерфейс API позволяют нам эффективно манипулировать страница; однако этот процесс может стать очень долгим и неэффективным по мере роста страниц.

DOM может превратиться в разочаровывающую игру в прятки по мере расширения HTML-документа. Особенно в связи с продвижением одностраничных приложений (SPA) модель DOM может стать огромной, и может потребоваться просмотреть тысячи тегов h1, чтобы найти конкретный элемент, который нужно изменить. Помимо беспокойства разработчика, производительность DOM может снизиться, поскольку обработчики событий JavaScript выполняют поиск среди тысячи тегов ‹h1› и проверяют, какие из них необходимо обновить. Должен быть лучший способ!

Виртуальный DOM лучше

Виртуальный дом более эффективен, потому что он абстрагирует DOM таким образом, что разработчики могут заранее запрограммировать, какие узлы будут обновляться. Это означает, что разработчики могут установить для узла определенное значение, и всякий раз, когда это значение изменяется, React за кулисами запускает все методы HTML DOM API, последовательно обновляя DOM в соответствии с этим измененным значением. Магия виртуального DOM заключается в том, что React делает этот процесс очень и очень эффективно: он как можно меньше меняется в реальном DOM. В конечном счете, виртуальный DOM способен выполнять эти эффективные изменения с помощью алгоритма, называемого сравнением или согласованием.

Примирение

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

1. Сначала React сравнивает корневые элементы двух деревьев.

Если корневые элементы относятся к разным типам (скажем, ‹div› по сравнению с ‹span›), React уничтожит старое дерево и полностью перестроит его. Когда дерево будет перестроено, экземпляры компонентов получат componentWillMount и componentDidMount.

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

3. Затем React будет перебирать списки дочерних элементов и корректировать дерево в любых точках различия.

React будет искать различия между двумя деревьями строка за строкой и будет изменять каждую строку старого дерева, которая не соответствует новому дереву. Это эффективно для изменений, в которых изменяется несколько строк, например, добавление в конец ‹ul›, где React просто вставит единственную отличающуюся строку.

Однако итеративный процесс React может стать неэффективным, когда несколько строк кажутся измененными: в приведенном ниже списке React не может распознать, что только одна строка вставлена ​​в середину списка, вместо этого он распознает каждую строку ниже ‹li›Maxwell‹. /li› как измененный и без необходимости перерисовывает их.

Решение этой неэффективности состоит в том, чтобы добавить уникальный ключ к каждому дочернему элементу. Когда у дочерних элементов есть ключи, React будет сравнивать дочерние элементы старого дерева и нового дерева на основе их ключей. Если в новом дереве нет дочернего элемента с ключом дочернего элемента в старом дереве, старый дочерний элемент будет размонтирован. В качестве альтернативы, если новое дерево содержит дочерний элемент с ключом, отсутствующим в старом дереве, будет смонтирован новый дочерний элемент. И, наконец, если у старого и нового дерева есть дочерний элемент с одним и тем же ключом, для принятия решения об обновлении экземпляра будет использоваться shouldUpdateReactComponent. Так что в примере со списком React сможет сказать, что дочерние элементы просто переместились, и поэтому не будет их перерисовывать.

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