From 203df39c304b5f89f4efa591d0e563bcb5d94155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Yves=20G=C3=A9rardy?= Date: Sun, 15 Apr 2018 18:34:08 +0200 Subject: [PATCH] Remove the DOM nodes recycling pool (fix #1653, fix #2023) --- docs/change-log.md | 1 + performance/test-perf.js | 9 +- render/render.js | 222 ++++++++-------------------- render/tests/test-component.js | 4 +- render/tests/test-onbeforeupdate.js | 2 +- render/tests/test-onremove.js | 6 +- render/tests/test-updateElement.js | 8 +- render/tests/test-updateNodes.js | 15 +- 8 files changed, 84 insertions(+), 183 deletions(-) diff --git a/docs/change-log.md b/docs/change-log.md index 81b48762..efd51c4b 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -31,6 +31,7 @@ - API: `m.request` supports `timeout` as attr - ([#1966](https://github.com/MithrilJS/mithril.js/pull/1966)) - Mocks: add limited support for the DOMParser API ([#2097](https://github.com/MithrilJS/mithril.js/pull/2097)) - API: add support for raw SVG in `m.trust()` string ([#2097](https://github.com/MithrilJS/mithril.js/pull/2097)) +- Internals: remove the DOM nodes recycling pool ([#2122](https://github.com/MithrilJS/mithril.js/pull/2122)) #### Bug fixes diff --git a/performance/test-perf.js b/performance/test-perf.js index 89160a05..a2b79421 100644 --- a/performance/test-perf.js +++ b/performance/test-perf.js @@ -358,16 +358,9 @@ var Root = { } } -suite.add({ - name : "repeated trees (recycling)", - fn : function () { - m.render(scratch, [m(Root)]) - m.render(scratch, []) - } -}) suite.add({ - name : "repeated trees (no recycling)", + name : "repeated trees", fn : function () { m.render(scratch, [m(Root)]) m.render(scratch, []) diff --git a/render/render.js b/render/render.js index 7a674c3c..2b05a00a 100644 --- a/render/render.js +++ b/render/render.js @@ -175,8 +175,6 @@ module.exports = function($window) { * @param {Vnode[] | null} old - the list of vnodes of the last `render()` call for * this part of the tree * @param {Vnode[] | null} vnodes - as above, but for the current `render()` call. - * @param {boolean} recyclingParent - was the parent vnode or one of its ancestor - * fetched from the recycling pool? * @param {Function[]} hooks - an accumulator of post-render hooks (oncreate/onupdate) * @param {Element | null} nextSibling - the next DOM node if we're dealing with a * fragment that is not the last item in its @@ -190,8 +188,7 @@ module.exports = function($window) { // // 1. describe its general structure // 2. focus on the diff algorithm optimizations - // 3. describe how the recycling pool meshes into this - // 4. discuss DOM node operations. + // 3. discuss DOM node operations. // ## Overview: // @@ -205,7 +202,6 @@ module.exports = function($window) { // - manages the leftovers: after diffing, are there: // - old nodes left to remove? // - new nodes to insert? - // - nodes left in the recycling pool? // deal with them! // // The lists are only iterated over once, with an exception for the nodes in `old` that @@ -213,10 +209,7 @@ module.exports = function($window) { // ## Diffing // - // There's first a simple diff for unkeyed lists of equal length that eschews the pool. - // - // It is followed by a small section that activates the recycling pool if present, we'll - // ignore it for now. + // There's first a simple diff for unkeyed lists of equal length. // // Then comes the main diff algorithm that is split in four parts (simplifying a bit). // @@ -244,109 +237,69 @@ module.exports = function($window) { // The range of old nodes that wasn't covered by the first three sections is passed to // `removeNodes()`. Those nodes are removed unless marked as `.skip: true`. // - // Then some pool business happens. - // // It should be noted that the description of the four sections above is not perfect, because those // parts are actually implemented as only two loops, one for the first two parts, and one for // the other two. I'm not sure it wins us anything except maybe a few bytes of file size. - // ## The pool - // - // `old.pool` is an optional array that holds the vnodes that have been previously removed - // from the DOM at this level (provided they met the pool eligibility criteria). - // - // If the `old`, `old.pool` and `vnodes` meet some criteria described in `isRecyclable`, the - // elements of the pool are appended to the `old` array, which enables the reconciler to find - // them. - // - // While this is optimal for unkeyed diff and map-based keyed diff (the fourth diff part), - // that strategy clashes with the second and third parts of the main diff algo, because - // the end of the old list is now filled with the nodes of the pool. - // - // To determine if a vnode was brought back from the pool, we look at its position in the - // `old` array (see the various `oFromPool` definitions). That information is important - // in three circumstances: - // - If the old and the new vnodes are the same object (`===`), diff is not performed unless - // the old node comes from the pool (since it must be recycled/re-created). - // - The value of `oFromPool` is passed as the `recycling` parameter of `updateNode()` (whether - // the parent is being recycled is also factred in here) - // - It is used in the DOM node insertion logic (see below) - // - // At the very end of `updateNodes()`, the nodes in the pool that haven't been picked back - // are put in the new pool for the next render phase. - // - // The pool eligibility and `isRecyclable()` criteria are to be updated as part of #1675. - // ## DOM node operations // // In most cases `updateNode()` and `createNode()` perform the DOM operations. However, - // this is not the case if the node moved (second and fourth part of the diff algo), or - // if the node was brough back from the pool and both the old and new nodes have the same - // `.tag` value (when the `.tag` differ, `updateNode()` does the insertion). + // this is not the case if the node moved (second and fourth part of the diff algo). // // The fourth part of the diff currently inserts nodes unconditionally, leading to issues // like #1791 and #1999. We need to be smarter about those situations where adjascent old // nodes remain together in the new list in a way that isn't covered by parts one and // three of the diff algo. - function updateNodes(parent, old, vnodes, recyclingParent, hooks, nextSibling, ns) { - if (old === vnodes && !recyclingParent || old == null && vnodes == null) return + function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) { + if (old === vnodes || old == null && vnodes == null) return else if (old == null) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, ns) - else if (vnodes == null) removeNodes(old, 0, old.length, vnodes, recyclingParent) + else if (vnodes == null) removeNodes(old, 0, old.length) else { - var start = 0, commonLength = Math.min(old.length, vnodes.length), originalOldLength = old.length, hasPool = false, isUnkeyed = false + var start = 0, commonLength = Math.min(old.length, vnodes.length), isUnkeyed = false for(; start < commonLength; start++) { if (old[start] != null && vnodes[start] != null) { if (old[start].key == null && vnodes[start].key == null) isUnkeyed = true break } } - if (isUnkeyed && originalOldLength === vnodes.length) { - for (start = 0; start < originalOldLength; start++) { - if (old[start] === vnodes[start] && !recyclingParent || old[start] == null && vnodes[start] == null) continue - else if (old[start] == null) createNode(parent, vnodes[start], hooks, ns, getNextSibling(old, start + 1, originalOldLength, nextSibling)) - else if (vnodes[start] == null) removeNodes(old, start, start + 1, vnodes, recyclingParent) - else updateNode(parent, old[start], vnodes[start], hooks, getNextSibling(old, start + 1, originalOldLength, nextSibling), recyclingParent, ns) + if (isUnkeyed && old.length === vnodes.length) { + for (start = 0; start < vnodes.length; start++) { + if (old[start] === vnodes[start] || old[start] == null && vnodes[start] == null) continue + else if (old[start] == null) createNode(parent, vnodes[start], hooks, ns, getNextSibling(old, start + 1, nextSibling)) + else if (vnodes[start] == null) removeNodes(old, start, start + 1) + else updateNode(parent, old[start], vnodes[start], hooks, getNextSibling(old, start + 1, nextSibling), ns) } return } - - if (isRecyclable(old, vnodes)) { - hasPool = true - old = old.concat(old.pool) - } - - var oldStart = start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map, o, v, oFromPool + var oldStart = start = 0, oldEnd = old.length - 1, end = vnodes.length - 1, map, o, v while (oldEnd >= oldStart && end >= start) { o = old[oldStart] v = vnodes[start] - oFromPool = hasPool && oldStart >= originalOldLength - if (o === v && !oFromPool && !recyclingParent || o == null && v == null) oldStart++, start++ + if (o === v || o == null && v == null) oldStart++, start++ else if (o == null) { if (isUnkeyed || v.key == null) { - createNode(parent, vnodes[start], hooks, ns, getNextSibling(old, ++start, originalOldLength, nextSibling)) + createNode(parent, vnodes[start], hooks, ns, getNextSibling(old, ++start, nextSibling)) } oldStart++ } else if (v == null) { if (isUnkeyed || o.key == null) { - removeNodes(old, start, start + 1, vnodes, recyclingParent) + removeNodes(old, start, start + 1) oldStart++ } start++ } else if (o.key === v.key) { oldStart++, start++ - updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, originalOldLength, nextSibling), oFromPool || recyclingParent, ns) - if (oFromPool && o.tag === v.tag) insertNode(parent, toFragment(v), nextSibling) + updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), ns) } else { o = old[oldEnd] - oFromPool = hasPool && oldEnd >= originalOldLength - if (o === v && !oFromPool && !recyclingParent) oldEnd--, start++ + if (o === v) oldEnd--, start++ else if (o == null) oldEnd-- else if (v == null) start++ else if (o.key === v.key) { - updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, originalOldLength, nextSibling), oFromPool || recyclingParent, ns) - if (oFromPool && o.tag === v.tag || start < end) insertNode(parent, toFragment(v), getNextSibling(old, oldStart, originalOldLength, nextSibling)) + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), ns) + if (start < end) insertNode(parent, toFragment(v), getNextSibling(old, oldStart, nextSibling)) oldEnd--, start++ } else break @@ -355,13 +308,11 @@ module.exports = function($window) { while (oldEnd >= oldStart && end >= start) { o = old[oldEnd] v = vnodes[end] - oFromPool = hasPool && oldEnd >= originalOldLength - if (o === v && !oFromPool && !recyclingParent) oldEnd--, end-- + if (o === v) oldEnd--, end-- else if (o == null) oldEnd-- else if (v == null) end-- else if (o.key === v.key) { - updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, originalOldLength, nextSibling), oFromPool || recyclingParent, ns) - if (oFromPool && o.tag === v.tag) insertNode(parent, toFragment(v), nextSibling) + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), ns) if (o.dom != null) nextSibling = o.dom oldEnd--, end-- } else { @@ -370,8 +321,7 @@ module.exports = function($window) { var oldIndex = map[v.key] if (oldIndex != null) { o = old[oldIndex] - oFromPool = hasPool && oldIndex >= originalOldLength - updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, originalOldLength, nextSibling), oFromPool || recyclingParent, ns) + updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), ns) insertNode(parent, toFragment(v), nextSibling) o.skip = true if (o.dom != null) nextSibling = o.dom @@ -385,43 +335,30 @@ module.exports = function($window) { if (end < start) break } createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns) - removeNodes(old, oldStart, Math.min(oldEnd + 1, originalOldLength), vnodes, recyclingParent) - if (hasPool) { - var limit = Math.max(oldStart, originalOldLength) - for (; oldEnd >= limit; oldEnd--) { - if (old[oldEnd].skip) old[oldEnd].skip = false - else addToPool(old[oldEnd], vnodes) - } - } + removeNodes(old, oldStart, oldEnd + 1) } } - // when recycling, we're re-using an old DOM node, but firing the oninit/oncreate hooks - // instead of onbeforeupdate/onupdate. - function updateNode(parent, old, vnode, hooks, nextSibling, recycling, ns) { + function updateNode(parent, old, vnode, hooks, nextSibling, ns) { var oldTag = old.tag, tag = vnode.tag if (oldTag === tag) { vnode.state = old.state vnode.events = old.events - if (!recycling && shouldNotUpdate(vnode, old)) return + if (shouldNotUpdate(vnode, old)) return if (typeof oldTag === "string") { if (vnode.attrs != null) { - if (recycling) { - vnode.state = {} - initLifecycle(vnode.attrs, vnode, hooks) - } - else updateLifecycle(vnode.attrs, vnode, hooks) + updateLifecycle(vnode.attrs, vnode, hooks) } switch (oldTag) { case "#": updateText(old, vnode); break case "<": updateHTML(parent, old, vnode, ns, nextSibling); break - case "[": updateFragment(parent, old, vnode, recycling, hooks, nextSibling, ns); break - default: updateElement(old, vnode, recycling, hooks, ns) + case "[": updateFragment(parent, old, vnode, hooks, nextSibling, ns); break + default: updateElement(old, vnode, hooks, ns) } } - else updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) + else updateComponent(parent, old, vnode, hooks, nextSibling, ns) } else { - removeNode(old, null, recycling) + removeNode(old) createNode(parent, vnode, hooks, ns, nextSibling) } } @@ -438,8 +375,8 @@ module.exports = function($window) { } else vnode.dom = old.dom, vnode.domSize = old.domSize } - function updateFragment(parent, old, vnode, recycling, hooks, nextSibling, ns) { - updateNodes(parent, old.children, vnode.children, recycling, hooks, nextSibling, ns) + function updateFragment(parent, old, vnode, hooks, nextSibling, ns) { + updateNodes(parent, old.children, vnode.children, hooks, nextSibling, ns) var domSize = 0, children = vnode.children vnode.dom = null if (children != null) { @@ -453,7 +390,7 @@ module.exports = function($window) { if (domSize !== 1) vnode.domSize = domSize } } - function updateElement(old, vnode, recycling, hooks, ns) { + function updateElement(old, vnode, hooks, ns) { var element = vnode.dom = old.dom ns = getNameSpace(vnode) || ns @@ -474,26 +411,22 @@ module.exports = function($window) { else { if (old.text != null) old.children = [Vnode("#", undefined, undefined, old.text, undefined, old.dom.firstChild)] if (vnode.text != null) vnode.children = [Vnode("#", undefined, undefined, vnode.text, undefined, undefined)] - updateNodes(element, old.children, vnode.children, recycling, hooks, null, ns) + updateNodes(element, old.children, vnode.children, hooks, null, ns) } } - function updateComponent(parent, old, vnode, hooks, nextSibling, recycling, ns) { - if (recycling) { - initComponent(vnode, hooks) - } else { - vnode.instance = Vnode.normalize(callHook.call(vnode.state.view, vnode)) - if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as argument") - if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks) - updateLifecycle(vnode.state, vnode, hooks) - } + function updateComponent(parent, old, vnode, hooks, nextSibling, ns) { + vnode.instance = Vnode.normalize(callHook.call(vnode.state.view, vnode)) + if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as argument") + if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks) + updateLifecycle(vnode.state, vnode, hooks) if (vnode.instance != null) { if (old.instance == null) createNode(parent, vnode.instance, hooks, ns, nextSibling) - else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling, ns) + else updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, ns) vnode.dom = vnode.instance.dom vnode.domSize = vnode.instance.domSize } else if (old.instance != null) { - removeNode(old.instance, null, recycling) + removeNode(old.instance) vnode.dom = undefined vnode.domSize = 0 } @@ -502,17 +435,6 @@ module.exports = function($window) { vnode.domSize = old.domSize } } - function isRecyclable(old, vnodes) { - if (old.pool != null && Math.abs(old.pool.length - vnodes.length) <= Math.abs(old.length - vnodes.length)) { - var oldChildrenLength = old[0] && old[0].children && old[0].children.length || 0 - var poolChildrenLength = old.pool[0] && old.pool[0].children && old.pool[0].children.length || 0 - var vnodesChildrenLength = vnodes[0] && vnodes[0].children && vnodes[0].children.length || 0 - if (Math.abs(poolChildrenLength - vnodesChildrenLength) <= Math.abs(oldChildrenLength - vnodesChildrenLength)) { - return true - } - } - return false - } function getKeyMap(vnodes, end) { var map = {}, i = 0 for (var i = 0; i < end; i++) { @@ -537,10 +459,8 @@ module.exports = function($window) { } else return vnode.dom } - // the vnodes array may hold items that come from the pool (after `limit`) they should - // be ignored - function getNextSibling(vnodes, i, limit, nextSibling) { - for (; i < limit; i++) { + function getNextSibling(vnodes, i, nextSibling) { + for (; i < vnodes.length; i++) { if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom } return nextSibling @@ -561,43 +481,37 @@ module.exports = function($window) { } //remove - function removeNodes(vnodes, start, end, context, recycling) { + function removeNodes(vnodes, start, end) { for (var i = start; i < end; i++) { var vnode = vnodes[i] if (vnode != null) { if (vnode.skip) vnode.skip = false - else removeNode(vnode, context, recycling) + else removeNode(vnode) } } } - // when a node is removed from a parent that's brought back from the pool, its hooks should - // not fire. - function removeNode(vnode, context, recycling) { + function removeNode(vnode) { var expected = 1, called = 0 - if (!recycling) { - var original = vnode.state - if (vnode.attrs && typeof vnode.attrs.onbeforeremove === "function") { - var result = callHook.call(vnode.attrs.onbeforeremove, vnode) - if (result != null && typeof result.then === "function") { - expected++ - result.then(continuation, continuation) - } + var original = vnode.state + if (vnode.attrs && typeof vnode.attrs.onbeforeremove === "function") { + var result = callHook.call(vnode.attrs.onbeforeremove, vnode) + if (result != null && typeof result.then === "function") { + expected++ + result.then(continuation, continuation) } - if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeremove === "function") { - var result = callHook.call(vnode.state.onbeforeremove, vnode) - if (result != null && typeof result.then === "function") { - expected++ - result.then(continuation, continuation) - } + } + if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeremove === "function") { + var result = callHook.call(vnode.state.onbeforeremove, vnode) + if (result != null && typeof result.then === "function") { + expected++ + result.then(continuation, continuation) } } continuation() function continuation() { if (++called === expected) { - if (!recycling) { - checkState(vnode, original) - onremove(vnode) - } + checkState(vnode, original) + onremove(vnode) if (vnode.dom) { var count = vnode.domSize || 1 if (count > 1) { @@ -607,7 +521,6 @@ module.exports = function($window) { } } removeNodeFromDOM(vnode.dom) - addToPool(vnode, context) } } } @@ -616,12 +529,6 @@ module.exports = function($window) { var parent = node.parentNode if (parent != null) parent.removeChild(node) } - function addToPool(vnode, context) { - if (context != null && vnode.domSize == null && !hasIntegrationMethods(vnode.attrs) && typeof vnode.tag === "string") { //TODO test custom elements - if (!context.pool) context.pool = [vnode] - else context.pool.push(vnode) - } - } function onremove(vnode) { if (vnode.attrs && typeof vnode.attrs.onremove === "function") callHook.call(vnode.attrs.onremove, vnode) if (typeof vnode.tag !== "string") { @@ -721,9 +628,6 @@ module.exports = function($window) { function isCustomElement(vnode){ return vnode.attrs.is || vnode.tag.indexOf("-") > -1 } - function hasIntegrationMethods(source) { - return source != null && (source.oncreate || source.onupdate || source.onbeforeremove || source.onremove) - } //style function updateStyle(element, old, style) { @@ -821,7 +725,7 @@ module.exports = function($window) { if (dom.vnodes == null) dom.textContent = "" if (!Array.isArray(vnodes)) vnodes = [vnodes] - updateNodes(dom, dom.vnodes, Vnode.normalizeChildren(vnodes), false, hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace) + updateNodes(dom, dom.vnodes, Vnode.normalizeChildren(vnodes), hooks, null, namespace === "http://www.w3.org/1999/xhtml" ? undefined : namespace) dom.vnodes = vnodes // document.activeElement can return null in IE https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement if (active != null && $doc.activeElement !== active) active.focus() diff --git a/render/tests/test-component.js b/render/tests/test-component.js index ea3d1624..dd27ef10 100644 --- a/render/tests/test-component.js +++ b/render/tests/test-component.js @@ -808,7 +808,7 @@ o.spec("component", function() { o(methods[hook].args.length).equals(attrs[hook].args.length)(hook) }) }) - o("recycled components get a fresh state", function() { + o("no recycling occurs (was: recycled components get a fresh state)", function() { var step = 0 var firstState var view = o.spy(function(vnode) { @@ -827,7 +827,7 @@ o.spec("component", function() { step = 1 render(root, [{tag: "div", children: [{tag: component, key: 1}]}]) - o(child).equals(root.firstChild.firstChild) + o(child).notEquals(root.firstChild.firstChild) // this used to be a recycling pool test o(view.callCount).equals(2) }) }) diff --git a/render/tests/test-onbeforeupdate.js b/render/tests/test-onbeforeupdate.js index d8b2f2d6..d0b1ecab 100644 --- a/render/tests/test-onbeforeupdate.js +++ b/render/tests/test-onbeforeupdate.js @@ -129,7 +129,7 @@ o.spec("onbeforeupdate", function() { render(root, temp) render(root, updated) - o(vnodes[0].dom).equals(updated[0].dom) + o(vnodes[0].dom).notEquals(updated[0].dom) // this used to be a recycling pool test o(updated[0].dom.nodeName).equals("DIV") o(onbeforeupdate.callCount).equals(0) }) diff --git a/render/tests/test-onremove.js b/render/tests/test-onremove.js index 5ab20caf..9b4c4e23 100644 --- a/render/tests/test-onremove.js +++ b/render/tests/test-onremove.js @@ -91,7 +91,7 @@ o.spec("onremove", function() { o(vnode.dom.attributes["onremove"]).equals(undefined) o(vnode.events).equals(undefined) }) - o("calls onremove on recycle", function() { + o("calls onremove on keyed nodes", function() { var remove = o.spy() var vnodes = [{tag: "div", key: 1}] var temp = [{tag: "div", key: 2, attrs: {onremove: remove}}] @@ -101,7 +101,7 @@ o.spec("onremove", function() { render(root, temp) render(root, updated) - o(vnodes[0].dom).equals(updated[0].dom) + o(vnodes[0].dom).notEquals(updated[0].dom) // this used to be a recycling pool test o(remove.callCount).equals(1) }) o("does not recycle when there's an onremove", function() { @@ -211,7 +211,7 @@ o.spec("onremove", function() { render(root, [temp]) render(root, [updated]) - o(vnode.dom).equals(updated.dom) + o(vnode.dom).notEquals(updated.dom) // this used to be a recycling pool test o(onremove.callCount).equals(1) }) }) diff --git a/render/tests/test-updateElement.js b/render/tests/test-updateElement.js index a2ae0fcb..0e0332b7 100644 --- a/render/tests/test-updateElement.js +++ b/render/tests/test-updateElement.js @@ -237,7 +237,7 @@ o.spec("updateElement", function() { o(updated.dom.firstChild.namespaceURI).equals("http://www.w3.org/2000/svg") }) - o("restores correctly when recycling", function() { + o("doesn't restore since we're not recycling", function() { var vnode = {tag: "div", key: 1} var updated = {tag: "div", key: 2} @@ -250,9 +250,9 @@ o.spec("updateElement", function() { var c = vnode.dom o(root.childNodes.length).equals(1) - o(a).equals(c) + o(a).notEquals(c) // this used to be a recycling pool test }) - o("restores correctly when recycling via map", function() { + o("doesn't restore since we're not recycling (via map)", function() { var a = {tag: "div", key: 1} var b = {tag: "div", key: 2} var c = {tag: "div", key: 3} @@ -269,6 +269,6 @@ o.spec("updateElement", function() { var y = root.childNodes[1] o(root.childNodes.length).equals(3) - o(x).equals(y) + o(x).notEquals(y) // this used to be a recycling pool test }) }) diff --git a/render/tests/test-updateNodes.js b/render/tests/test-updateNodes.js index 65d81916..6983766e 100644 --- a/render/tests/test-updateNodes.js +++ b/render/tests/test-updateNodes.js @@ -776,7 +776,7 @@ o.spec("updateNodes", function() { o(root.childNodes[0].childNodes[1].childNodes.length).equals(1) o(root.childNodes[1].childNodes.length).equals(0) }) - o("recycles", function() { + o("doesn't recycle", function() { var vnodes = [{tag: "div", key: 1}] var temp = [] var updated = [{tag: "div", key: 1}] @@ -785,10 +785,10 @@ o.spec("updateNodes", function() { render(root, temp) render(root, updated) - o(vnodes[0].dom).equals(updated[0].dom) + o(vnodes[0].dom).notEquals(updated[0].dom) // this used to be a recycling pool test o(updated[0].dom.nodeName).equals("DIV") }) - o("recycles when not keyed", function() { + o("doesn't recycle when not keyed", function() { var vnodes = [{tag: "div"}] var temp = [] var updated = [{tag: "div"}] @@ -798,19 +798,22 @@ o.spec("updateNodes", function() { render(root, updated) o(root.childNodes.length).equals(1) - o(vnodes[0].dom).equals(updated[0].dom) + o(vnodes[0].dom).notEquals(updated[0].dom) // this used to be a recycling pool test o(updated[0].dom.nodeName).equals("DIV") }) - o("recycles deep", function() { + o("doesn't recycle deep", function() { var vnodes = [{tag: "div", children: [{tag: "a", key: 1}]}] var temp = [{tag: "div"}] var updated = [{tag: "div", children: [{tag: "a", key: 1}]}] render(root, vnodes) + + var oldChild = vnodes[0].dom.firstChild + render(root, temp) render(root, updated) - o(vnodes[0].dom.firstChild).equals(updated[0].dom.firstChild) + o(oldChild).notEquals(updated[0].dom.firstChild) // this used to be a recycling pool test o(updated[0].dom.firstChild.nodeName).equals("A") }) o("mixed unkeyed tags are not broken by recycle", function() {