-
Notifications
You must be signed in to change notification settings - Fork 5
/
vue-vdom-extended.js
103 lines (90 loc) · 2.92 KB
/
vue-vdom-extended.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Create virtual node
export function h(tag, props, children) {
// Return the virtual node
return {
tag,
props,
children,
};
}
// Mount a virtual node to the DOM
export function mount(vnode, container) {
// Create the element
const el = document.createElement(vnode.tag);
vnode.el = el;
// Set properties & event listeners
for (const key in vnode.props) {
if (key.startsWith('on')) {
// Handle event listeners
// key = onClick
// key.slice(2).toLowerCase = click
// vnode.props[key] = function
el.addEventListener(key.slice(2).toLowerCase(), vnode.props[key]);
} else {
// Handle attributes
el.setAttribute(key, vnode.props[key]);
}
}
// Handle children
if (typeof vnode.children === 'string') {
el.textContent = vnode.children;
} else {
vnode.children.forEach(child => {
mount(child, el);
});
}
// Mount to the DOM
container.appendChild(el);
}
// Unmount a virtual node from the DOM
export function unmount(vnode) {
vnode.el.parentNode.removeChild(vnode.el);
}
// Take 2 virtual nodes, compare & figure out what's the difference
export function patch(n1, n2) {
const el = (n2.el = n1.el);
// Case where the nodes are of different tags
if (n1.tag !== n2.tag) {
mount(n2, el.parentNode);
unmount(n1);
}
// Case where the nodes are of the same tag
else {
// New virtual node has string children
if (typeof n2.children === 'string') {
el.textContent = n2.children;
}
// New virtual node has array children
else {
// Old virtual node has string children
if (typeof n1.children === 'string') {
el.textContent = '';
n2.children.forEach(child => mount(child, el));
}
// Case where the new vnode has string children
else {
const c1 = n1.children;
const c2 = n2.children;
const commonLength = Math.min(c1.length, c2.length);
// Patch the children both nodes have in common
for (let i = 0; i < commonLength; i++) {
patch(c1[i], c2[i]);
}
// Old children was longer
// Remove the children that are not "there" anymore
if (c1.length > c2.length) {
c1.slice(c2.length).forEach(child => {
unmount(child);
});
}
// Old children was shorter
// Add the newly added children
else if (c2.length > c1.length) {
c2.slice(c1.length).forEach(child => {
mount(child, el);
});
}
}
}
}
}