Создайте свои собственные Vue.js с нуля – часть 3 (построение VDOM)
Если вам понравилась эта статья, скорее всего, вы хотели бы, на что я тоже твит. Если вам интересно, посмотрите на мой Профиль Twitter . 🚀
Это третья часть серии называется Создайте свои собственные vue.js с нуля Где я научу вас создавать основы реактивной структуры, такими как Vue.js. Чтобы следить за этим постом в блоге, я предлагаю вам прочитать о Первый и Второй Часть этой серии.
Этот пост может быть длинным сначала, но, вероятно, не так, как он выглядит. Он описывает каждый шаг кода, поэтому он выглядит довольно сложно. Но нести со мной, все это сделает идеальный смысл в конце 😊
- Введение
- Виртуальные базы DOM
- Реализация виртуального DOM & Rendering (этот пост)
- Строительная реактивность
- Принося все это вместе
Скелет
В Вторая часть этой серии Мы узнали о основах того, как работает виртуальный дом. Вы копируете каркас VDOM с последней точки от Этот гид Отказ Мы используем этот код для выполнения. Вы также найдете там готовую версию двигателя VDOM. Я также создал Кодепен , где вы можете играть с ним.
Создание виртуального узла
Итак, чтобы создать виртуальный узел, нам нужен тег , свойства и дети Отказ Итак, наша функция выглядит что-то подобное:
function h(tag, props, children){ ... }
(В Vue, функция для создания виртуальных узлов называется h Так вот как мы собираемся назвать это здесь.)
В этой функции нам нужен объект JavaScript из следующей структуры.
{
tag: 'div',
props: {
class: 'container'
},
children: ...
}
Для достижения этого мы должны обернуть параметры тега, свойств и детских узлов в объекте и вернуть его:
function h(tag, props, children) {
return {
tag,
props,
children,
}
}
Это уже для создания виртуального узла.
Установите виртуальный узел до DOM
Что я имею в виду с Гора Виртуальный узел до DOM представляет его к любому данному контейнеру. Этот узел может быть оригинальным контейнером (в нашем примере, #app -Дов) или другой виртуальный узел, на котором он будет установлен (например, Mountaing A внутри ).
Это будет рекурсивная функция, потому что нам придется пройти через все детские узлы и Гора к соответствующим контейнерам.
Наше Гора Функция будет выглядеть так:
function mount(vnode, container) { ... }
1) Нам нужно создать элемент DOM
const el = (vnode.el = document.createElement(vnode.tag))
2) Нам нужно установить свойства (реквизиты) в качестве атрибутов к элементу DOM:
Мы делаем это, итерация по ним, вроде таких:
for (const key in vnode.props) {
el.setAttribute(key, vnode.props[key])
}
3) Нам нужно монтировать детей внутри элемента
Помните, есть два типа детей:
- Простой текст
- Массив виртуальных узлов
Мы обрабатываем обоими:
// Children is a string/text
if (typeof vnode.children === 'string') {
el.textContent = vnode.children
}
// Chilren are virtual nodes
else {
vnode.children.forEach(child => {
mount(child, el) // Recursively mount the children
})
}
Как вы можете видеть во второй части этого кода, дети устанавливаются с тем же Гора функция. Это продолжается рекурсивно, пока осталось только «текстовые узлы». Тогда рекурсия останавливается.
В качестве последней части этой монтажной функции нам нужно добавить созданный элемент DOM в соответствующий контейнер:
container.appendChild(el)
Размонтируйте виртуальный узел из дома
В Размонтировать Функция, мы вынимаем данный виртуальный узел от своего родителя в реальном доме. Функция принимает только виртуальный узел в качестве параметра.
function unmount(vnode) {
vnode.el.parentNode.removeChild(vnode.el)
}
Патч виртуальный узел
Это означает, что принимают два виртуальных узла, сравните их и определить, в чем разница между ними.
Это безусловно, самая обширная функция, которую мы напишем для виртуального дома, но несут со мной.
1) Назначьте элемент DOM, с которым мы будем работать
const el = (n2.el = n1.el)
2) Проверьте, являются ли узлы разных тегов
Если узлы имеют разные теги, мы можем предположить, что содержание совершенно другое, и мы просто заменим узел целиком. Мы делаем это, установив новый узел и размонтируя старый.
if (n1.tag !== n2.tag) {
// Replace node
mount(n2, el.parentNode)
unmount(n1)
} else {
// Nodes have different tags
}
Если узлы имеют те же теги; Однако это может означать две разные вещи:
- Новый узел имеет струнные дети
- Новый узел имеет массив детей
3) случай, когда узел имеет рядные дети
В этом случае мы просто идем вперед и замените TextContent элемента с «детьми» (что на самом деле является просто строкой).
...
// Nodes have different tags
if (typeof n2.children === 'string') {
el.textContent = n2.children
}
...
4) Если узел имеет массив детей
В этом случае мы должны проверить различия между детьми. Есть три сценария:
- Длина детей одинакова
- У старого узема больше детей, чем новый узел. В этом случае нам нужно удалить «превышать» детей из дома
- Новый узел имеет больше детей, чем старый узел. В этом случае нам нужно добавить дополнительные дети в домо.
Итак, во-первых, нам нужно определить Общая длина детей, или в других условиях, минимальные дети считают каждый из узлов:
const c1 = n1.children const c2 = n2.children const commonLength = Math.min(c1.length, c2.length)
5) Патч общих детей
Для каждого из случаев из точки 4) нам нужно патч Дети, которые имеют общие узлы:
for (let i = 0; i < commonLength; i++) {
patch(c1[i], c2[i])
}
В случае, когда длины равны, это уже это. Нечего делать.
6) Удалить ненужных детей из дома
Если новый узел имеет меньше детей, чем старый узел, эти должны быть удалены из DOM. Мы уже написали Размонтировать Функция для этого, так что теперь нам нужно повторять лишние дети и размонтировать их:
if (c1.length > c2.length) {
c1.slice(c2.length).forEach(child => {
unmount(child)
})
}
7) Добавьте дополнительные дети в домо
Если новый узел имеет больше детей, чем старый узел, нам нужно добавить их в DOM. Мы также уже написали Гора функция для этого. Сейчас нам нужно переживать дополнительные дети и смонтировать их:
else if (c2.length > c1.length) {
c2.slice(c1.length).forEach(child => {
mount(child, el)
})
}
Вот и все. Мы нашли все различие между узлами и соответствующим образом исправили Дом. Однако это решение не реализуется, является исправлением свойств. Это сделало бы пост в блоге еще дольше и пропустит точку зрения.
Рендеринг виртуального дерева в реальном доме
Наш виртуальный двигатель DOM готов сейчас. Чтобы продемонстрировать его, мы можем создать некоторые узлы и сделать их. Предположим, мы хотим следующую HTML-структуру:
Hello World 🌍
Thanks for reading the marc.dev blog 😊
1) Создайте виртуальный узел H
const node1 = h('div', { class: 'container' }, [
h('div', null, 'X'),
h('span', null, 'hello'),
h('span', null, 'world'),
])
2) Установите узел до DOM
Мы хотим монтировать вновь созданные домо. Где? К #приложение -Дов в самом верхней части файла:
mount(node1, document.getElementById('app'))
Результат должен выглядеть что-то вроде этого:
3) Создайте второй виртуальный узел
Теперь мы можем создать второй узел с некоторыми изменениями в этом. Давайте добавим несколько узлов, чтобы результат будет это:
Это код для создания этого узла:
const node2 = h('div', { class: 'container' }, [
h('h1', null, 'Hello Dev 💻'),
h('p', null, [
h('span', null, 'Thanks for reading the '),
h('a', { href: 'https://marc.dev' }, 'marc.dev'),
h('span', null, ' blog'),
]),
h(
'img',
{
src: 'https://media.giphy.com/media/26gsjCZpPolPr3sBy/giphy.gif',
style: 'width: 350px; border-radius: 0.5rem;',
},
[],
),
])
Как видите, мы добавили некоторые узлы, а также изменили узел.
4) Визуализировать второй узел
Мы хотим заменить первый узел во втором, поэтому мы не используем Гора Отказ То, что мы хотим сделать, это выяснить разницу между двумя, вносит изменения, а затем сделать его. Итак, мы патч Это:
setTimeout(() => {
patch(node1, node2)
}, 3000)
Я добавил тайм-аут здесь, так что вы можете увидеть код DOM. Если нет, вы увидите только новую визуализацию VDOM.
Вот и все! У нас очень базовая версия двигателя DOM, который позволяет нам:
- Создать виртуальные узлы
- Монтировать виртуальные узлы до DOM
- Удалить виртуальные узлы из дома
- Найдите различия между двумя виртуальными узлами и обновите DOM соответственно
Вы можете найти код, который мы сделали в этом посте, на Github Gist я подготовил к тебе . Если вы просто хотите играть с ним, я также создал Кодепен Так что вы можете сделать это.
Если у вас есть еще вопросы об этом, не стесняйтесь добраться до меня через Twitter Отказ
Оригинальная обложка фото Джошуа Эрл на unplash, отредактировано Marc Bestes. .
Оригинал: “https://dev.to/themarcba/create-your-own-vue-js-from-scratch-part-2-building-the-vdom-3bp2”