Виртуальный DOM
Вы, наверное, слышали о термине «виртуальный DOM», на котором основана система рендеринга Vue.
Виртуальный DOM (VDOM) — это концепция программирования, при которой идеальное или «виртуальное» представление пользовательского интерфейса хранится в памяти и синхронизируется с «реальным» DOM. Эта концепция была впервые предложена React и была принята во многих других средах с различными реализациями, включая Vue.
Virtual DOM — это скорее шаблон, чем конкретная технология, поэтому не существует единой канонической реализации. Мы можем проиллюстрировать эту идею на простом примере
Здесь vnode представлен простой объект JavaScript («виртуальный узел»), представляющий элемент div. Он содержит всю информацию, необходимую нам для создания фактического элемента. Он также содержит больше дочерних узлов, что делает его корнем виртуального дерева DOM.
Средство визуализации во время выполнения может пройти по виртуальному дереву DOM и построить из него настоящее дерево DOM. Этот процесс называется монтированием
Если у нас есть две копии виртуальных деревьев DOM, средство визуализации также может просмотреть и сравнить два дерева, выявляя различия, и применить эти изменения к реальному DOM. Этот процесс называется патчем , также известным как «различение» или «согласование»
Основное преимущество виртуального DOM заключается в том, что он дает разработчику возможность программно создавать, проверять и компоновать желаемые структуры пользовательского интерфейса декларативным способом, оставляя при этом прямое манипулирование DOM рендереру
Конвейер рендеринга
На высоком уровне это то, что происходит при монтировании компонента Vue
- Компиляция : шаблоны Vue компилируются в функции рендеринга : функции, которые возвращают виртуальные деревья DOM. Этот шаг можно выполнить либо заранее на этапе сборки, либо «на лету» с помощью компилятора среды выполнения.
- Mount : средство визуализации во время выполнения вызывает функции рендеринга, просматривает возвращенное виртуальное дерево DOM и создает на его основе фактические узлы DOM. Этот шаг выполняется как реактивный эффект , поэтому он отслеживает все использованные реактивные зависимости.
- Исправление : при изменении зависимости, используемой во время монтирования, эффект запускается повторно. На этот раз создается новое, обновленное дерево Virtual DOM. Средство визуализации во время выполнения просматривает новое дерево, сравнивает его со старым и применяет необходимые обновления к фактическому DOM.

Шаблоны против функций рендеринга
Шаблоны Vue компилируются в виртуальные функции рендеринга DOM. Vue также предоставляет API, которые позволяют нам пропустить этап компиляции шаблона и напрямую создавать функции рендеринга. Функции рендеринга более гибки, чем шаблоны, при работе с высокодинамической логикой, поскольку вы можете работать с виртуальными узлами, используя всю мощь JavaScript.
Так почему же Vue по умолчанию рекомендует шаблоны? Есть множество причин:
- Шаблоны ближе к реальному HTML. Это упрощает повторное использование существующих фрагментов HTML, применение лучших практик обеспечения специальных возможностей, стилизацию с помощью CSS, а также понимание и изменение дизайнеров.
- Шаблоны легче статически анализировать из-за их более детерминированного синтаксиса. Это позволяет компилятору шаблонов Vue применять множество оптимизаций во время компиляции для повышения производительности виртуального DOM (о чем мы поговорим ниже).
На практике шаблонов достаточно для большинства случаев использования в приложениях. Функции рендеринга обычно используются только в повторно используемых компонентах, которым приходится иметь дело с высокодинамической логикой рендеринга. Использование функции рендеринга более подробно будет рассмотрено в уроке «Функции рендеринга и JSX»
Виртуальный DOM, информированный компилятором
Реализация виртуального DOM в React и большинстве других реализаций виртуального DOM является чисто средой выполнения: алгоритм согласования не может делать никаких предположений о входящем виртуальном дереве DOM, поэтому он должен полностью пройти по дереву и сравнить реквизиты каждого vnode, чтобы гарантировать правильность. Кроме того, даже если часть дерева никогда не меняется, для нее всегда создаются новые vnodes при каждом повторном рендеринге, что приводит к ненужной нагрузке на память. Это один из наиболее критикуемых аспектов виртуального DOM: несколько грубый процесс согласования жертвует эффективностью в обмен на декларативность и корректность.
Но так не должно быть. В Vue фреймворк контролирует как компилятор, так и среду выполнения. Это позволяет нам реализовать множество оптимизаций времени компиляции, которыми может воспользоваться только тесно связанный рендерер. Компилятор может статически анализировать шаблон и оставлять подсказки в сгенерированном коде, чтобы среда выполнения могла использовать ярлыки, когда это возможно. В то же время мы по-прежнему сохраняем возможность пользователю перейти на уровень функции рендеринга для более прямого управления в крайних случаях. Мы называем этот гибридный подход Compiler-Informed Virtual DOM .
Ниже мы обсудим несколько основных оптимизаций, выполненных компилятором шаблонов Vue для улучшения производительности виртуального DOM во время выполнения.
Статический подъем
Довольно часто в шаблоне встречаются части, не содержащие каких-либо динамических привязок
Элементы div foo и bar являются статическими — повторное создание vnodes и их сравнение при каждом повторном рендеринге не требуется. Компилятор Vue автоматически извлекает вызовы создания vnode из функции рендеринга и повторно использует одни и те же vnode при каждом рендеринге. Средство визуализации также может полностью пропустить их различие, когда заметит, что старый vnode и новый vnode совпадают.
Кроме того, если имеется достаточное количество последовательных статических элементов, они будут сжаты в один «статический vnode», который содержит простую строку HTML для всех этих узлов ( Пример ). Эти статические vnodes монтируются путем непосредственной установки innerHTML. Они также кэшируют соответствующие узлы DOM при первоначальном монтировании — если один и тот же фрагмент контента повторно используется где-либо в приложении, новые узлы DOM создаются с использованием Native cloneNode(), что чрезвычайно эффективно.
Флаги исправлений
Для одного элемента с динамическими привязками мы также можем получить из него много информации во время компиляции:
При генерации кода функции рендеринга для этих элементов Vue кодирует тип обновления, который необходим каждому из них, непосредственно в вызове создания vnode
Последний аргумент 2 — это флаг исправления . Элемент может иметь несколько флагов исправлений, которые будут объединены в одно число. Затем средство визуализации во время выполнения может проверить флаги с помощью побитовых операций , чтобы определить, нужно ли ему выполнять определенную работу
Побитовые проверки выполняются очень быстро. Благодаря флагам исправлений Vue может выполнить минимально необходимый объем работы при обновлении элементов с динамическими привязками.
Vue также кодирует тип дочерних узлов vnode. Например, шаблон, имеющий несколько корневых узлов, представлен как фрагмент. В большинстве случаев мы точно знаем, что порядок этих корневых узлов никогда не изменится, поэтому эту информацию также можно предоставить среде выполнения в виде флага исправления:
Таким образом, среда выполнения может полностью пропустить согласование дочерних порядков для корневого фрагмента.
Выравнивание деревьев
Еще раз взглянув на сгенерированный код из предыдущего примера, вы заметите, что корень возвращенного виртуального дерева DOM создается с помощью специального createElementBlock() вызова:
Концептуально «блок» — это часть шаблона, имеющая устойчивую внутреннюю структуру. В этом случае весь шаблон состоит из одного блока, поскольку он не содержит никаких структурных директив, таких как v-if и v-for
Каждый блок отслеживает все узлы-потомки (а не только прямые дочерние узлы), имеющие флаги исправления. Например:
Результатом является плоский массив, содержащий только динамические узлы-потомки:
Когда этому компоненту необходимо выполнить повторную визуализацию, ему нужно пройти только по сплющенному дереву, а не по всему дереву. Это называется Tree Flattening , и оно значительно уменьшает количество узлов, которые необходимо пройти во время виртуального согласования DOM. Любые статические части шаблона фактически пропускаются.
v-if и v-for директивы создадут новые узлы блока:
Дочерний блок отслеживается внутри массива динамических потомков родительского блока. Это сохраняет стабильную структуру родительского блока.
Влияние на гидратацию SSR
И флаги исправлений, и выравнивание деревьев также значительно улучшают производительность Vue SSR Hydration
- Гидратация одного элемента может осуществляться быстрыми способами на основе соответствующего флага исправления vnode.
- Во время гидратации необходимо проходить только узлы блоков и их динамические потомки, эффективно достигая частичной гидратации на уровне шаблона.