fix diff bugs when there are null components in list
This commit is contained in:
parent
7227cc546f
commit
276184484d
4 changed files with 100 additions and 31 deletions
36
mithril.js
36
mithril.js
|
|
@ -328,10 +328,13 @@ var renderService = function($window) {
|
||||||
if (vnode.instance != null) {
|
if (vnode.instance != null) {
|
||||||
var element = createNode(vnode.instance, hooks, ns)
|
var element = createNode(vnode.instance, hooks, ns)
|
||||||
vnode.dom = vnode.instance.dom
|
vnode.dom = vnode.instance.dom
|
||||||
vnode.domSize = vnode.instance.domSize
|
vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0
|
||||||
return element
|
return element
|
||||||
}
|
}
|
||||||
else return $emptyFragment
|
else {
|
||||||
|
vnode.domSize = 0
|
||||||
|
return $emptyFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//update
|
//update
|
||||||
function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) {
|
function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) {
|
||||||
|
|
@ -341,12 +344,12 @@ var renderService = function($window) {
|
||||||
else {
|
else {
|
||||||
var recycling = isRecyclable(old, vnodes)
|
var recycling = isRecyclable(old, vnodes)
|
||||||
if (recycling) old = old.concat(old.pool)
|
if (recycling) old = old.concat(old.pool)
|
||||||
|
|
||||||
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
|
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
|
||||||
while (oldEnd >= oldStart && end >= start) {
|
while (oldEnd >= oldStart && end >= start) {
|
||||||
var o = old[oldStart], v = vnodes[start]
|
var o = old[oldStart], v = vnodes[start]
|
||||||
if (o === v) oldStart++, start++
|
if (o === v) oldStart++, start++
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
else if (o != null && v != null && o.key === v.key && o.tag === v.tag) {
|
||||||
oldStart++, start++
|
oldStart++, start++
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
|
||||||
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
||||||
|
|
@ -354,7 +357,7 @@ var renderService = function($window) {
|
||||||
else {
|
else {
|
||||||
var o = old[oldEnd]
|
var o = old[oldEnd]
|
||||||
if (o === v) oldEnd--, start++
|
if (o === v) oldEnd--, start++
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
else if (o != null && v != null && o.key === v.key && o.tag === v.tag) {
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||||
insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
|
insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
|
||||||
oldEnd--, start++
|
oldEnd--, start++
|
||||||
|
|
@ -365,10 +368,10 @@ var renderService = function($window) {
|
||||||
while (oldEnd >= oldStart && end >= start) {
|
while (oldEnd >= oldStart && end >= start) {
|
||||||
var o = old[oldEnd], v = vnodes[end]
|
var o = old[oldEnd], v = vnodes[end]
|
||||||
if (o === v) oldEnd--, end--
|
if (o === v) oldEnd--, end--
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
else if (o != null && v != null && o.key === v.key && o.tag === v.tag) {
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||||
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
||||||
nextSibling = o.dom
|
if (o.dom != null) nextSibling = o.dom
|
||||||
oldEnd--, end--
|
oldEnd--, end--
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -380,7 +383,7 @@ var renderService = function($window) {
|
||||||
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||||
insertNode(parent, toFragment(movable), nextSibling)
|
insertNode(parent, toFragment(movable), nextSibling)
|
||||||
old[oldIndex].skip = true
|
old[oldIndex].skip = true
|
||||||
nextSibling = movable.dom
|
if (movable.dom != null) nextSibling = movable.dom
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var dom = createNode(v, hooks, undefined)
|
var dom = createNode(v, hooks, undefined)
|
||||||
|
|
@ -440,7 +443,7 @@ var renderService = function($window) {
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
for (var i = 0; i < children.length; i++) {
|
for (var i = 0; i < children.length; i++) {
|
||||||
var child = children[i]
|
var child = children[i]
|
||||||
if (child != null) {
|
if (child != null && child.dom != null) {
|
||||||
if (vnode.dom == null) vnode.dom = child.dom
|
if (vnode.dom == null) vnode.dom = child.dom
|
||||||
domSize += child.domSize || 1
|
domSize += child.domSize || 1
|
||||||
}
|
}
|
||||||
|
|
@ -479,7 +482,12 @@ var renderService = function($window) {
|
||||||
}
|
}
|
||||||
else if (old.instance != null) {
|
else if (old.instance != null) {
|
||||||
removeNode(parent, old.instance, null, false)
|
removeNode(parent, old.instance, null, false)
|
||||||
vnode.dom = vnode.domSize = undefined
|
vnode.dom = undefined
|
||||||
|
vnode.domSize = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vnode.dom = old.dom
|
||||||
|
vnode.domSize = old.domSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function isRecyclable(old, vnodes) {
|
function isRecyclable(old, vnodes) {
|
||||||
|
|
@ -506,7 +514,7 @@ var renderService = function($window) {
|
||||||
}
|
}
|
||||||
function toFragment(vnode) {
|
function toFragment(vnode) {
|
||||||
var count = vnode.domSize
|
var count = vnode.domSize
|
||||||
if (count != null) {
|
if (count != null || vnode.dom == null) {
|
||||||
var fragment = $doc.createDocumentFragment()
|
var fragment = $doc.createDocumentFragment()
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
var dom = vnode.dom
|
var dom = vnode.dom
|
||||||
|
|
@ -519,7 +527,7 @@ var renderService = function($window) {
|
||||||
}
|
}
|
||||||
function getNextSibling(vnodes, i, nextSibling) {
|
function getNextSibling(vnodes, i, nextSibling) {
|
||||||
for (; i < vnodes.length; i++) {
|
for (; i < vnodes.length; i++) {
|
||||||
if (vnodes[i] != null) return vnodes[i].dom
|
if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom
|
||||||
}
|
}
|
||||||
return nextSibling
|
return nextSibling
|
||||||
}
|
}
|
||||||
|
|
@ -699,12 +707,12 @@ var renderService = function($window) {
|
||||||
function copy(data) {
|
function copy(data) {
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
var output = []
|
var output = []
|
||||||
for (var i = 0; i < data.length; i++) output[i] = copy(data[i])
|
for (var i = 0; i < data.length; i++) output[i] = data[i]
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
else if (typeof data === "object") {
|
else if (typeof data === "object") {
|
||||||
var output = {}
|
var output = {}
|
||||||
for (var i in data) output[i] = copy(data[i])
|
for (var i in data) output[i] = data[i]
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
|
|
|
||||||
|
|
@ -98,10 +98,13 @@ module.exports = function($window) {
|
||||||
if (vnode.instance != null) {
|
if (vnode.instance != null) {
|
||||||
var element = createNode(vnode.instance, hooks, ns)
|
var element = createNode(vnode.instance, hooks, ns)
|
||||||
vnode.dom = vnode.instance.dom
|
vnode.dom = vnode.instance.dom
|
||||||
vnode.domSize = vnode.instance.domSize
|
vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0
|
||||||
return element
|
return element
|
||||||
}
|
}
|
||||||
else return $emptyFragment
|
else {
|
||||||
|
vnode.domSize = 0
|
||||||
|
return $emptyFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//update
|
//update
|
||||||
|
|
@ -112,12 +115,12 @@ module.exports = function($window) {
|
||||||
else {
|
else {
|
||||||
var recycling = isRecyclable(old, vnodes)
|
var recycling = isRecyclable(old, vnodes)
|
||||||
if (recycling) old = old.concat(old.pool)
|
if (recycling) old = old.concat(old.pool)
|
||||||
|
|
||||||
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
|
var oldStart = 0, start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map
|
||||||
while (oldEnd >= oldStart && end >= start) {
|
while (oldEnd >= oldStart && end >= start) {
|
||||||
var o = old[oldStart], v = vnodes[start]
|
var o = old[oldStart], v = vnodes[start]
|
||||||
if (o === v) oldStart++, start++
|
if (o === v) oldStart++, start++
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
else if (o != null && v != null && o.key === v.key && o.tag === v.tag) {
|
||||||
oldStart++, start++
|
oldStart++, start++
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling, ns)
|
||||||
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
||||||
|
|
@ -125,7 +128,7 @@ module.exports = function($window) {
|
||||||
else {
|
else {
|
||||||
var o = old[oldEnd]
|
var o = old[oldEnd]
|
||||||
if (o === v) oldEnd--, start++
|
if (o === v) oldEnd--, start++
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
else if (o != null && v != null && o.key === v.key && o.tag === v.tag) {
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||||
insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
|
insertNode(parent, toFragment(o), getNextSibling(old, oldStart, nextSibling))
|
||||||
oldEnd--, start++
|
oldEnd--, start++
|
||||||
|
|
@ -136,10 +139,10 @@ module.exports = function($window) {
|
||||||
while (oldEnd >= oldStart && end >= start) {
|
while (oldEnd >= oldStart && end >= start) {
|
||||||
var o = old[oldEnd], v = vnodes[end]
|
var o = old[oldEnd], v = vnodes[end]
|
||||||
if (o === v) oldEnd--, end--
|
if (o === v) oldEnd--, end--
|
||||||
else if (o != null && v != null && o.key === v.key) {
|
else if (o != null && v != null && o.key === v.key && o.tag === v.tag) {
|
||||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||||
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
if (recycling && o.tag === v.tag) insertNode(parent, toFragment(o), nextSibling)
|
||||||
nextSibling = o.dom
|
if (o.dom != null) nextSibling = o.dom
|
||||||
oldEnd--, end--
|
oldEnd--, end--
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -151,7 +154,7 @@ module.exports = function($window) {
|
||||||
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
updateNode(parent, movable, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling, ns)
|
||||||
insertNode(parent, toFragment(movable), nextSibling)
|
insertNode(parent, toFragment(movable), nextSibling)
|
||||||
old[oldIndex].skip = true
|
old[oldIndex].skip = true
|
||||||
nextSibling = movable.dom
|
if (movable.dom != null) nextSibling = movable.dom
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var dom = createNode(v, hooks, undefined)
|
var dom = createNode(v, hooks, undefined)
|
||||||
|
|
@ -211,7 +214,7 @@ module.exports = function($window) {
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
for (var i = 0; i < children.length; i++) {
|
for (var i = 0; i < children.length; i++) {
|
||||||
var child = children[i]
|
var child = children[i]
|
||||||
if (child != null) {
|
if (child != null && child.dom != null) {
|
||||||
if (vnode.dom == null) vnode.dom = child.dom
|
if (vnode.dom == null) vnode.dom = child.dom
|
||||||
domSize += child.domSize || 1
|
domSize += child.domSize || 1
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +253,12 @@ module.exports = function($window) {
|
||||||
}
|
}
|
||||||
else if (old.instance != null) {
|
else if (old.instance != null) {
|
||||||
removeNode(parent, old.instance, null, false)
|
removeNode(parent, old.instance, null, false)
|
||||||
vnode.dom = vnode.domSize = undefined
|
vnode.dom = undefined
|
||||||
|
vnode.domSize = 0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vnode.dom = old.dom
|
||||||
|
vnode.domSize = old.domSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function isRecyclable(old, vnodes) {
|
function isRecyclable(old, vnodes) {
|
||||||
|
|
@ -277,7 +285,7 @@ module.exports = function($window) {
|
||||||
}
|
}
|
||||||
function toFragment(vnode) {
|
function toFragment(vnode) {
|
||||||
var count = vnode.domSize
|
var count = vnode.domSize
|
||||||
if (count != null) {
|
if (count != null || vnode.dom == null) {
|
||||||
var fragment = $doc.createDocumentFragment()
|
var fragment = $doc.createDocumentFragment()
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
var dom = vnode.dom
|
var dom = vnode.dom
|
||||||
|
|
@ -290,7 +298,7 @@ module.exports = function($window) {
|
||||||
}
|
}
|
||||||
function getNextSibling(vnodes, i, nextSibling) {
|
function getNextSibling(vnodes, i, nextSibling) {
|
||||||
for (; i < vnodes.length; i++) {
|
for (; i < vnodes.length; i++) {
|
||||||
if (vnodes[i] != null) return vnodes[i].dom
|
if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom
|
||||||
}
|
}
|
||||||
return nextSibling
|
return nextSibling
|
||||||
}
|
}
|
||||||
|
|
@ -479,12 +487,12 @@ module.exports = function($window) {
|
||||||
function copy(data) {
|
function copy(data) {
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
var output = []
|
var output = []
|
||||||
for (var i = 0; i < data.length; i++) output[i] = copy(data[i])
|
for (var i = 0; i < data.length; i++) output[i] = data[i]
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
else if (typeof data === "object") {
|
else if (typeof data === "object") {
|
||||||
var output = {}
|
var output = {}
|
||||||
for (var i in data) output[i] = copy(data[i])
|
for (var i in data) output[i] = data[i]
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
|
|
|
||||||
|
|
@ -640,10 +640,11 @@ o.spec("component", function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
o.spec("state", function() {
|
o.spec("state", function() {
|
||||||
o("deep copies state", function() {
|
o("copies state", function() {
|
||||||
var called = 0
|
var called = 0
|
||||||
|
var data = {a: 1}
|
||||||
var component = {
|
var component = {
|
||||||
data: [{a: 1}],
|
data: data,
|
||||||
oninit: init,
|
oninit: init,
|
||||||
view: function() {
|
view: function() {
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -653,7 +654,27 @@ o.spec("component", function() {
|
||||||
render(root, [{tag: component}])
|
render(root, [{tag: component}])
|
||||||
|
|
||||||
function init(vnode) {
|
function init(vnode) {
|
||||||
o(vnode.state.data).deepEquals([{a: 1}])
|
o(vnode.state.data).deepEquals(data)
|
||||||
|
o(vnode.state.data).equals(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
o("state copy is shallow", function() {
|
||||||
|
var called = 0
|
||||||
|
var body = {a: 1}
|
||||||
|
var data = [body]
|
||||||
|
var component = {
|
||||||
|
data: data,
|
||||||
|
oninit: init,
|
||||||
|
view: function() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(root, [{tag: component}])
|
||||||
|
|
||||||
|
function init(vnode) {
|
||||||
|
o(vnode.state.data).equals(data)
|
||||||
|
o(vnode.state.data[0]).equals(body)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -813,4 +813,36 @@ o.spec("updateNodes", function() {
|
||||||
o(root.childNodes[0].nodeName).equals("A")
|
o(root.childNodes[0].nodeName).equals("A")
|
||||||
o(root.childNodes[1].nodeName).equals("B")
|
o(root.childNodes[1].nodeName).equals("B")
|
||||||
})
|
})
|
||||||
|
o("fragment child toggles from null when followed by null component then tag", function() {
|
||||||
|
var component = {view: function() {return null}}
|
||||||
|
var vnodes = [{tag: "[", children: [{tag: "a"}, {tag: component}, {tag: "b"}]}]
|
||||||
|
var temp = [{tag: "[", children: [null, {tag: component}, {tag: "b"}]}]
|
||||||
|
var updated = [{tag: "[", children: [{tag: "a"}, {tag: component}, {tag: "b"}]}]
|
||||||
|
|
||||||
|
render(root, vnodes)
|
||||||
|
render(root, temp)
|
||||||
|
render(root, updated)
|
||||||
|
|
||||||
|
o(root.childNodes.length).equals(2)
|
||||||
|
o(root.childNodes[0].nodeName).equals("A")
|
||||||
|
o(root.childNodes[1].nodeName).equals("B")
|
||||||
|
})
|
||||||
|
o("fragment child toggles from null in component when followed by null component then tag", function() {
|
||||||
|
var flag = true
|
||||||
|
var a = {view: function() {return flag ? {tag: "a"} : null}}
|
||||||
|
var b = {view: function() {return null}}
|
||||||
|
var vnodes = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
|
||||||
|
var temp = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
|
||||||
|
var updated = [{tag: "[", children: [{tag: a}, {tag: b}, {tag: "s"}]}]
|
||||||
|
|
||||||
|
render(root, vnodes)
|
||||||
|
flag = false
|
||||||
|
render(root, temp)
|
||||||
|
flag = true
|
||||||
|
render(root, updated)
|
||||||
|
|
||||||
|
o(root.childNodes.length).equals(2)
|
||||||
|
o(root.childNodes[0].nodeName).equals("A")
|
||||||
|
o(root.childNodes[1].nodeName).equals("S")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue