diff algorithm special cases diffing of virtual styles

This commit is contained in:
Andreas Rumpf 2017-06-05 10:21:38 +02:00
commit a9b5216a42

View file

@ -123,22 +123,26 @@ proc replaceById(id: cstring; newTree: Node) =
x.parentNode.replaceChild(newTree, x) x.parentNode.replaceChild(newTree, x)
#newTree.id = id #newTree.id = id
proc equalsShallow(a, b: VNode): bool = type
if a.kind != b.kind: return false EqResult = enum
if a.id != b.id: return false different, similar, identical
if a.key != b.key: return false
proc equalsShallow(a, b: VNode): EqResult =
if a.kind != b.kind: return different
if a.id != b.id: return different
if a.key != b.key: return different
if a.kind == VNodeKind.text: if a.kind == VNodeKind.text:
if a.text != b.text: return false if a.text != b.text: return different
elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk: elif a.kind == VNodeKind.vthunk or a.kind == VNodeKind.dthunk:
if a.text != b.text: return false if a.text != b.text: return different
if a.len != b.len: return false if a.len != b.len: return different
for i in 0..<a.len: for i in 0..<a.len:
if not equalsShallow(a[i], b[i]): return false if equalsShallow(a[i], b[i]) == different: return different
if not sameAttrs(a, b): return false if not sameAttrs(a, b): return different
if a.class != b.class: return false if a.class != b.class: return different
if a.style != b.style: return false if a.style != b.style: return similar
# XXX test event listeners here? # Do not test event listeners here!
return true return identical
proc equalsTree(a, b: VNode): bool = proc equalsTree(a, b: VNode): bool =
when false: when false:
@ -167,59 +171,68 @@ proc updateDirtyElements(parent, current: Node, newNode: VNode) =
#if dirtyCount <= 0: return #if dirtyCount <= 0: return
proc updateElement(parent, current: Node, newNode, oldNode: VNode) = proc updateElement(parent, current: Node, newNode, oldNode: VNode) =
if not equalsShallow(newNode, oldNode): let res = equalsShallow(newNode, oldNode)
if res == different:
detach(oldNode) detach(oldNode)
let n = vnodeToDom(newNode) let n = vnodeToDom(newNode)
if parent == nil: if parent == nil:
replaceById("ROOT", n) replaceById("ROOT", n)
else: else:
parent.replaceChild(n, current) parent.replaceChild(n, current)
elif newNode.kind != VNodeKind.text: else:
let newLength = newNode.len if res == similar:
var oldLength = oldNode.len # we keep the oldNode, but take over the style from the new node:
let minLength = min(newLength, oldLength) if oldNode.dom != nil:
assert oldNode.kind == newNode.kind if newNode.style != nil: applyStyle(oldNode.dom, newNode.style)
when defined(simpleDiff): else: oldNode.dom.style = Style()
for i in 0..min(newLength, oldLength)-1: oldNode.style = newNode.style
updateElement(current, current[i], newNode[i], oldNode[i])
if newLength > oldLength: if newNode.kind != VNodeKind.text:
for i in oldLength..newLength-1: let newLength = newNode.len
current.appendChild(vnodeToDom(newNode[i])) var oldLength = oldNode.len
elif oldLength > newLength: let minLength = min(newLength, oldLength)
for i in countdown(oldLength-1, newLength): assert oldNode.kind == newNode.kind
when defined(simpleDiff):
for i in 0..min(newLength, oldLength)-1:
updateElement(current, current[i], newNode[i], oldNode[i])
if newLength > oldLength:
for i in oldLength..newLength-1:
current.appendChild(vnodeToDom(newNode[i]))
elif oldLength > newLength:
for i in countdown(oldLength-1, newLength):
detach(oldNode[i])
current.removeChild(current.lastChild)
else:
var commonPrefix = 0
while commonPrefix < minLength and
equalsTree(newNode[commonPrefix], oldNode[commonPrefix]):
inc commonPrefix
var oldPos = oldLength - 1
var newPos = newLength - 1
while oldPos >= commonPrefix and newPos >= commonPrefix and
equalsTree(newNode[newPos], oldNode[oldPos]):
dec oldPos
dec newPos
var pos = min(oldPos, newPos) + 1
for i in commonPrefix..pos-1:
updateElement(current, current.childNodes[i], newNode[i], oldNode[i])
var nextChildPos = oldPos + 1
while pos <= newPos:
if nextChildPos == oldLength:
current.appendChild(vnodeToDom(newNode[pos]))
else:
current.insertBefore(vnodeToDom(newNode[pos]), current.childNodes[nextChildPos])
# added new Node, so old state of VDOM have one more Node
inc oldLength
inc pos
inc nextChildPos
for i in pos..oldPos:
detach(oldNode[i]) detach(oldNode[i])
current.removeChild(current.lastChild) current.removeChild(current.childNodes[pos])
else:
var commonPrefix = 0
while commonPrefix < minLength and
equalsTree(newNode[commonPrefix], oldNode[commonPrefix]):
inc commonPrefix
var oldPos = oldLength - 1
var newPos = newLength - 1
while oldPos >= commonPrefix and newPos >= commonPrefix and
equalsTree(newNode[newPos], oldNode[oldPos]):
dec oldPos
dec newPos
var pos = min(oldPos, newPos) + 1
for i in commonPrefix..pos-1:
updateElement(current, current.childNodes[i], newNode[i], oldNode[i])
var nextChildPos = oldPos + 1
while pos <= newPos:
if nextChildPos == oldLength:
current.appendChild(vnodeToDom(newNode[pos]))
else:
current.insertBefore(vnodeToDom(newNode[pos]), current.childNodes[nextChildPos])
# added new Node, so old state of VDOM have one more Node
inc oldLength
inc pos
inc nextChildPos
for i in pos..oldPos:
detach(oldNode[i])
current.removeChild(current.childNodes[pos])
when false: when false:
var drawTimeout: Timeout var drawTimeout: Timeout