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

Изначально меня вдохновил доклад Стефана Джудиса на Web Rebels 2018, так что не стесняйтесь взглянуть на него здесь.

Эта концепция Virtual DOM стала популярной еще в 2013 году, когда был выпущен React. Благодаря этой концепции ReactJS является одной из сверхбыстрых библиотек для создания пользовательского интерфейса. Я постараюсь объяснить это несколькими предложениями, а затем мы напишем собственное.

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

Хорошо, вот план, как мы будем создавать нашу виртуальную DOM.

  1. Создайте функцию hyperscript для рендеринга DOM - это своего рода JSX
  2. Создать простое приложение с гиперкриптом
  3. Сделайте наше приложение динамичным и научитесь рендерить виртуальную DOM
  4. Реализуйте алгоритм сравнения, чтобы увидеть мощь виртуальной DOM

Реализация HyperScript

Если вы когда-либо работали с ReactJS, вы, вероятно, знаете, что такое JSX. Это может быть еще одна тема для обсуждения, но вскоре он преобразует «HTML-подобный» синтаксис в вызовы функций JavaScript, в React он передается как React.createElement. Итак, на этом этапе наша цель - воссоздать эту замечательную функцию.

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

Входными данными для этой функции являются: тип узла, свойства (также известные как атрибуты), дочерние элементы. Вот простая реализация этой функции:

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

Примечание: на самом деле мы могли бы пойти дальше и рекурсивно отрисовывать дочерние элементы, но для простоты я оставил это на потом.

Вот как это можно использовать (с этого момента мы будем использовать h вместо гиперкрипт):

Создание приложения с помощью Hyperscript

Хорошо, теперь мы можем создать простое приложение с помощью Hyperscript. Давайте создадим более сложное приложение, чем было на предыдущем шаге. Вот наша новая функция App.

Когда приложение выполняется, оно создает div с двумя дочерними элементами: один отображает заголовок H1, а второй - неупорядоченный список. Обратите внимание, что мы передаем props нашей функции и отображаем props.list в неупорядоченном списке. Давайте добавим еще немного магии рендеринга:

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

Это было не так уж и сложно. Это? Давайте добавим динамический контент и случайные смайлы каждую секунду, чтобы мы могли видеть, как отображается наше приложение.

Реализовать рендеринг vDOM

Хорошо, теперь у нас есть динамическое приложение с hyperscript, давайте перейдем к реальной виртуальной модели DOM и ее реализации. Прежде всего нам нужно изменить нашу функцию hyperscript. Теперь он не должен создавать настоящую DOM, а вместо этого должен создавать виртуальную DOM. Итак, учитывая nodeName, атрибуты и дочерние элементы, мы просто создаем объект с соответствующими ключами. Благодаря ES6 мы можем сделать это одной строкой:

У нас есть виртуальный DOM, и если мы выполним функцию App с тем же списком смайлов, мы получим что-то вроде этого (авторизованный в консоли):

Очень похоже на DOM. Теперь давайте создадим функцию, которая преобразует виртуальный DOM в реальный DOM. Как вы уже догадались, он должен принимать в качестве параметра виртуальный узел. Вот:

Позвольте мне объяснить, что он делает, шаг за шагом:

  1. Используя деструктуризацию, мы получаем имя узла, атрибуты и дочерние элементы виртуального узла.
  2. Если vnode - это текст (мы можем проверить это с помощью vnode.split), то мы возвращаем текстовый узел
  3. В противном случае мы создаем элемент с nodeName и устанавливаем его атрибуты из объекта attributes
  4. Сделайте то же самое с детьми, если есть

Теперь вспомните нашу функцию render, которая визуализировала наше приложение? Нам просто нужно немного изменить, чтобы это работало:

Эта статья стала немного длиннее, чем я думал, поэтому я решил разбить ее на две части.

Итак, давайте подведем итоги. Мы создали hyperscript - виртуальную фабрику DOM , renderNode - , которая превращает виртуальную DOM в элемент DOM и функциональный компонент Приложение, который создает всю страницу. Результат такой же, как и раньше, без виртуального DOM, но теперь у нас больше контроля. В следующей статье мы рассмотрим, что делает React (и виртуальный DOM) таким быстрым.

Вы можете просмотреть все шаги в моем репозитории GitHub. Вы можете найти эти шаги в ветках.

В следующей статье мы реализуем простой алгоритм сравнения, который ускорит работу нашего приложения и вы сможете увидеть его действие.

ОБНОВЛЕНИЕ: Здравствуйте, если вам понравилось, посмотрите вторую часть этой статьи!

ОБНОВЛЕНИЕ 24.11.2019: если вы хотите изучить React, выполняя множество упражнений, не стесняйтесь подписаться на мой предстоящий курс 30-day-React.