В этой статье я хочу обобщить свой опыт создания собственного «виртуального DOM». Звучит слишком амбициозно? Возможно, но это не так сложно, как вы думаете. Как сказано в названии, это будет иметь смысл, когда вы создадите свой собственный, а не читаете тысячи статей об этом.
Изначально меня вдохновил доклад Стефана Джудиса на Web Rebels 2018, так что не стесняйтесь взглянуть на него здесь.
Эта концепция Virtual DOM стала популярной еще в 2013 году, когда был выпущен React. Благодаря этой концепции ReactJS является одной из сверхбыстрых библиотек для создания пользовательского интерфейса. Я постараюсь объяснить это несколькими предложениями, а затем мы напишем собственное.
Виртуальный DOM - это представление DOM как объекта. При изменении состояния приложения новая виртуальная модель DOM сравнивается (с применением алгоритмов сравнения) с моделью DOM, и отражаются только изменения, не вызывающие полного повторного рендеринга DOM.
Хорошо, вот план, как мы будем создавать нашу виртуальную DOM.
- Создайте функцию hyperscript для рендеринга DOM - это своего рода JSX
- Создать простое приложение с гиперкриптом
- Сделайте наше приложение динамичным и научитесь рендерить виртуальную DOM
- Реализуйте алгоритм сравнения, чтобы увидеть мощь виртуальной 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. Как вы уже догадались, он должен принимать в качестве параметра виртуальный узел. Вот:
Позвольте мне объяснить, что он делает, шаг за шагом:
- Используя деструктуризацию, мы получаем имя узла, атрибуты и дочерние элементы виртуального узла.
- Если vnode - это текст (мы можем проверить это с помощью vnode.split), то мы возвращаем текстовый узел
- В противном случае мы создаем элемент с nodeName и устанавливаем его атрибуты из объекта attributes
- Сделайте то же самое с детьми, если есть
Теперь вспомните нашу функцию render, которая визуализировала наше приложение? Нам просто нужно немного изменить, чтобы это работало:
Эта статья стала немного длиннее, чем я думал, поэтому я решил разбить ее на две части.
Итак, давайте подведем итоги. Мы создали hyperscript - виртуальную фабрику DOM , renderNode - , которая превращает виртуальную DOM в элемент DOM и функциональный компонент Приложение, который создает всю страницу. Результат такой же, как и раньше, без виртуального DOM, но теперь у нас больше контроля. В следующей статье мы рассмотрим, что делает React (и виртуальный DOM) таким быстрым.
Вы можете просмотреть все шаги в моем репозитории GitHub. Вы можете найти эти шаги в ветках.
В следующей статье мы реализуем простой алгоритм сравнения, который ускорит работу нашего приложения и вы сможете увидеть его действие.
ОБНОВЛЕНИЕ: Здравствуйте, если вам понравилось, посмотрите вторую часть этой статьи!
ОБНОВЛЕНИЕ 24.11.2019: если вы хотите изучить React, выполняя множество упражнений, не стесняйтесь подписаться на мой предстоящий курс 30-day-React.