forked from dongyuanxin/pure-virtual-dom
-
Notifications
You must be signed in to change notification settings - Fork 0
/
diff.js
77 lines (69 loc) · 2.01 KB
/
diff.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
const CHANGE_TYPE_TEXT = Symbol('text')
const CHANGE_TYPE_PROP = Symbol('props')
const CHANGE_TYPE_REPLACE = Symbol('replace')
function checkChangeType(newNode, oldNode) {
if (typeof newNode !== typeof oldNode || newNode.tagName !== oldNode.tagName) {
return CHANGE_TYPE_REPLACE;
}
if (typeof newNode === 'string') {
if (newNode !== oldNode) {
return CHANGE_TYPE_TEXT;
}
return;
}
const propsChanged = Reflect
.ownKeys(newNode.props)
.reduce(
(prev, name) => prev || (oldNode.props[name] !== newNode.props[name]),
false
);
if (propsChanged) {
return CHANGE_TYPE_PROP;
}
return;
}
/**
*
* @param {HTMLElement} $node
* @param {Object} removedAttrs
* @param {Object} newAttrWithVals
*/
function replaceAttribute($node, removedAttrs, newAttrs) {
if (!$node) {
return
}
Reflect.ownKeys(removedAttrs)
.forEach(attr => $node.removeAttribute(attr))
Reflect.ownKeys(newAttrs)
.forEach(attr => $node.setAttribute(attr, newAttrs[attr]))
}
function updateEl($parent, newNode, oldNode, index = 0) {
let changeType = null;
if (!oldNode) {
$parent.appendChild(newNode.render());
} else if (!newNode) {
$parent.removeChild($parent.childNodes[index]);
} else if (changeType = checkChangeType(newNode, oldNode)) {
if (changeType === CHANGE_TYPE_TEXT) {
$parent.replaceChild(
document.createTextNode(newNode),
$parent.childNodes[index]
);
} else if (changeType === CHANGE_TYPE_REPLACE) {
$parent.replaceChild(newNode.render(), $parent.childNodes[index]);
} else if (changeType === CHANGE_TYPE_PROP) {
replaceAttribute($parent.childNodes[index], oldNode.props, newNode.props)
}
} else if (newNode.tagName) {
const newLength = newNode.children.length;
const oldLength = oldNode.children.length;
for (let i = 0; i < newLength || i < oldLength; ++i) {
updateEl(
$parent.childNodes[index],
newNode.children[i],
oldNode.children[i],
i
);
}
}
}