Optimized children redrawing, resetting $key on redraw.
h3rald h3rald@h3rald.com
Fri, 01 May 2020 14:42:19 +0200
5 files changed,
124 insertions(+),
95 deletions(-)
M
__tests__/vnode.js
→
__tests__/vnode.js
@@ -129,15 +129,21 @@ expect(span).toEqual(node.childNodes[1]);
}); it("should provide a redraw method that is able to remove existing DOM nodes", () => { - const newvnode = h3("div", [h3("span")]); - const oldvnode = h3("div", [h3("span#a"), h3("span")]); - const node = oldvnode.render(); - const span = node.childNodes[1]; + let oldvnode = h3("div", [h3("span#a"), h3("span")]); + let newvnode = h3("div", [h3("span")]); + let node = oldvnode.render(); oldvnode.redraw({ node: node, vnode: newvnode }); expect(oldvnode).toEqual(newvnode); expect(oldvnode.children.length).toEqual(1); expect(node.childNodes.length).toEqual(1); - expect(span).toEqual(node.childNodes[0]); + oldvnode = h3("div.test-children", [h3("span.a"), h3("span.b")]); + node = oldvnode.render(); + newvnode = h3("div.test-children", [h3("div.c")]); + oldvnode.redraw({ node: node, vnode: newvnode }); + expect(oldvnode).toEqual(newvnode); + expect(oldvnode.children.length).toEqual(1); + expect(node.childNodes.length).toEqual(1); + expect(oldvnode.children[0].classList[0]).toEqual("c"); }); it("should provide a redraw method that is able to figure out differences in children", () => {
M
docs/H3_DeveloperGuide.htm
→
docs/H3_DeveloperGuide.htm
@@ -7999,7 +7999,7 @@ </ul>
</div> <div id="footer"> - <p><span class="copy"></span> Fabio Cevasco – April 26, 2020</p> + <p><span class="copy"></span> Fabio Cevasco – May 1, 2020</p> <p><span>Powered by</span> <a href="https://h3rald.com/hastyscribe"><span class="hastyscribe"></span></a></p> </div> </div>
M
docs/example/assets/js/h3.js
→
docs/example/assets/js/h3.js
@@ -314,6 +314,8 @@ node.parentNode.replaceChild(newvnode.render(), node);
oldvnode.from(newvnode); return; } + // $key + oldvnode.$key = newvnode.$key; // ID if (oldvnode.id !== newvnode.id) { node.id = newvnode.id || "";@@ -398,14 +400,15 @@ });
oldvnode.eventListeners = newvnode.eventListeners; } // Children - var newmap = []; // Map positions of newvnode children in relation to oldvnode children - var oldmap = []; // Map positions of oldvnode children in relation to newvnode children function mapChildren(parent1, parent2) { const map = []; for (let j = 0; j < parent1.children.length; j++) { let found = false; for (let k = 0; k < parent2.children.length; k++) { - if (parent1.children[j].equal(parent2.children[k])) { + if ( + parent1.children[j].equal(parent2.children[k]) && + !map.includes(k) + ) { map.push(k); found = true; break;@@ -418,40 +421,43 @@ }
} return map; } - var newmap = mapChildren(newvnode, oldvnode); - var oldmap = mapChildren(oldvnode, newvnode); - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); - if (equal(newmap, oldmap) && (notFoundInNew >= 0 || notFoundInOld >= 0)) { + // Map positions of newvnode children in relation to oldvnode children + let newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + let oldmap = mapChildren(oldvnode, newvnode); + let notFoundInOld = newmap.indexOf(-1); + let notFoundInNew = oldmap.indexOf(-1); + if (newmap.length === oldmap.length && notFoundInNew >= 0) { // Something changed for (let i = 0; i < newmap.length; i++) { - if (newmap[i] === -1) { - if (oldvnode.children[i].type === "#text") { - oldvnode.children[i] = newvnode.children[i]; - node.childNodes[i].nodeValue = newvnode.children[i].value; - } else { - oldvnode.children[i].redraw({ - node: node.childNodes[i], - vnode: newvnode.children[i], - }); - } + if (newmap[i] === -1 || oldmap[i] === -1) { + oldvnode.children[i].redraw({ + node: node.childNodes[i], + vnode: newvnode.children[i], + }); } } } else { - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); while (notFoundInOld >= 0 || notFoundInNew >= 0) { // First remove children not found in new map, then add the missing ones. if (notFoundInNew >= 0) { - // while there are children not found in newvnode, remove them and re-check - node.removeChild(node.childNodes[notFoundInNew]); - oldvnode.children.splice(notFoundInNew, 1); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); - } - if (notFoundInOld >= 0) { + const childOfNew = + newvnode.children.length > notFoundInNew && + newvnode.children[notFoundInNew]; + const childofOld = oldvnode.children[notFoundInNew]; + if (childOfNew && childofOld && childofOld.type === childOfNew.type) { + // optimization to avoid removing nodes of the same type + oldvnode.children[notFoundInNew].redraw({ + node: node.childNodes[notFoundInNew], + vnode: newvnode.children[notFoundInNew], + }); + } else { + // while there are children not found in newvnode, remove them and re-check + node.removeChild(node.childNodes[notFoundInNew]); + oldvnode.children.splice(notFoundInNew, 1); + } + } else { + //(notFoundInOld >= 0) { // while there are children not found in oldvnode, add them and re-check node.insertBefore( newvnode.children[notFoundInOld].render(),@@ -462,11 +468,11 @@ notFoundInOld,
0, newvnode.children[notFoundInOld] ); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); } + newmap = mapChildren(newvnode, oldvnode); + oldmap = mapChildren(oldvnode, newvnode); + notFoundInNew = oldmap.indexOf(-1); + notFoundInOld = newmap.indexOf(-1); } } // innerHTML
M
docs/js/h3.js
→
docs/js/h3.js
@@ -314,6 +314,8 @@ node.parentNode.replaceChild(newvnode.render(), node);
oldvnode.from(newvnode); return; } + // $key + oldvnode.$key = newvnode.$key; // ID if (oldvnode.id !== newvnode.id) { node.id = newvnode.id || "";@@ -398,14 +400,15 @@ });
oldvnode.eventListeners = newvnode.eventListeners; } // Children - var newmap = []; // Map positions of newvnode children in relation to oldvnode children - var oldmap = []; // Map positions of oldvnode children in relation to newvnode children function mapChildren(parent1, parent2) { const map = []; for (let j = 0; j < parent1.children.length; j++) { let found = false; for (let k = 0; k < parent2.children.length; k++) { - if (parent1.children[j].equal(parent2.children[k])) { + if ( + parent1.children[j].equal(parent2.children[k]) && + !map.includes(k) + ) { map.push(k); found = true; break;@@ -418,40 +421,43 @@ }
} return map; } - var newmap = mapChildren(newvnode, oldvnode); - var oldmap = mapChildren(oldvnode, newvnode); - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); - if (equal(newmap, oldmap) && (notFoundInNew >= 0 || notFoundInOld >= 0)) { + // Map positions of newvnode children in relation to oldvnode children + let newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + let oldmap = mapChildren(oldvnode, newvnode); + let notFoundInOld = newmap.indexOf(-1); + let notFoundInNew = oldmap.indexOf(-1); + if (newmap.length === oldmap.length && notFoundInNew >= 0) { // Something changed for (let i = 0; i < newmap.length; i++) { - if (newmap[i] === -1) { - if (oldvnode.children[i].type === "#text") { - oldvnode.children[i] = newvnode.children[i]; - node.childNodes[i].nodeValue = newvnode.children[i].value; - } else { - oldvnode.children[i].redraw({ - node: node.childNodes[i], - vnode: newvnode.children[i], - }); - } + if (newmap[i] === -1 || oldmap[i] === -1) { + oldvnode.children[i].redraw({ + node: node.childNodes[i], + vnode: newvnode.children[i], + }); } } } else { - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); while (notFoundInOld >= 0 || notFoundInNew >= 0) { // First remove children not found in new map, then add the missing ones. if (notFoundInNew >= 0) { - // while there are children not found in newvnode, remove them and re-check - node.removeChild(node.childNodes[notFoundInNew]); - oldvnode.children.splice(notFoundInNew, 1); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); - } - if (notFoundInOld >= 0) { + const childOfNew = + newvnode.children.length > notFoundInNew && + newvnode.children[notFoundInNew]; + const childofOld = oldvnode.children[notFoundInNew]; + if (childOfNew && childofOld && childofOld.type === childOfNew.type) { + // optimization to avoid removing nodes of the same type + oldvnode.children[notFoundInNew].redraw({ + node: node.childNodes[notFoundInNew], + vnode: newvnode.children[notFoundInNew], + }); + } else { + // while there are children not found in newvnode, remove them and re-check + node.removeChild(node.childNodes[notFoundInNew]); + oldvnode.children.splice(notFoundInNew, 1); + } + } else { + //(notFoundInOld >= 0) { // while there are children not found in oldvnode, add them and re-check node.insertBefore( newvnode.children[notFoundInOld].render(),@@ -462,11 +468,11 @@ notFoundInOld,
0, newvnode.children[notFoundInOld] ); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); } + newmap = mapChildren(newvnode, oldvnode); + oldmap = mapChildren(oldvnode, newvnode); + notFoundInNew = oldmap.indexOf(-1); + notFoundInOld = newmap.indexOf(-1); } } // innerHTML
M
h3.js
→
h3.js
@@ -314,6 +314,8 @@ node.parentNode.replaceChild(newvnode.render(), node);
oldvnode.from(newvnode); return; } + // $key + oldvnode.$key = newvnode.$key; // ID if (oldvnode.id !== newvnode.id) { node.id = newvnode.id || "";@@ -398,14 +400,15 @@ });
oldvnode.eventListeners = newvnode.eventListeners; } // Children - var newmap = []; // Map positions of newvnode children in relation to oldvnode children - var oldmap = []; // Map positions of oldvnode children in relation to newvnode children function mapChildren(parent1, parent2) { const map = []; for (let j = 0; j < parent1.children.length; j++) { let found = false; for (let k = 0; k < parent2.children.length; k++) { - if (parent1.children[j].equal(parent2.children[k]) && !map.includes(k)) { + if ( + parent1.children[j].equal(parent2.children[k]) && + !map.includes(k) + ) { map.push(k); found = true; break;@@ -418,11 +421,13 @@ }
} return map; } - var newmap = mapChildren(newvnode, oldvnode); - var oldmap = mapChildren(oldvnode, newvnode); - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); - if (newmap.length === oldmap.length && (notFoundInNew >= 0 || notFoundInOld >= 0)) { + // Map positions of newvnode children in relation to oldvnode children + let newmap = mapChildren(newvnode, oldvnode); + // Map positions of oldvnode children in relation to newvnode children + let oldmap = mapChildren(oldvnode, newvnode); + let notFoundInOld = newmap.indexOf(-1); + let notFoundInNew = oldmap.indexOf(-1); + if (newmap.length === oldmap.length && notFoundInNew >= 0) { // Something changed for (let i = 0; i < newmap.length; i++) { if (newmap[i] === -1 || oldmap[i] === -1) {@@ -433,20 +438,26 @@ });
} } } else { - var notFoundInOld = newmap.indexOf(-1); - var notFoundInNew = oldmap.indexOf(-1); while (notFoundInOld >= 0 || notFoundInNew >= 0) { // First remove children not found in new map, then add the missing ones. if (notFoundInNew >= 0) { - // while there are children not found in newvnode, remove them and re-check - node.removeChild(node.childNodes[notFoundInNew]); - oldvnode.children.splice(notFoundInNew, 1); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); - } - if (notFoundInOld >= 0) { + const childOfNew = + newvnode.children.length > notFoundInNew && + newvnode.children[notFoundInNew]; + const childofOld = oldvnode.children[notFoundInNew]; + if (childOfNew && childofOld && childofOld.type === childOfNew.type) { + // optimization to avoid removing nodes of the same type + oldvnode.children[notFoundInNew].redraw({ + node: node.childNodes[notFoundInNew], + vnode: newvnode.children[notFoundInNew], + }); + } else { + // while there are children not found in newvnode, remove them and re-check + node.removeChild(node.childNodes[notFoundInNew]); + oldvnode.children.splice(notFoundInNew, 1); + } + } else { + //(notFoundInOld >= 0) { // while there are children not found in oldvnode, add them and re-check node.insertBefore( newvnode.children[notFoundInOld].render(),@@ -457,11 +468,11 @@ notFoundInOld,
0, newvnode.children[notFoundInOld] ); - newmap = mapChildren(newvnode, oldvnode); - oldmap = mapChildren(oldvnode, newvnode); - notFoundInNew = oldmap.indexOf(-1); - notFoundInOld = newmap.indexOf(-1); } + newmap = mapChildren(newvnode, oldvnode); + oldmap = mapChildren(oldvnode, newvnode); + notFoundInNew = oldmap.indexOf(-1); + notFoundInOld = newmap.indexOf(-1); } } // innerHTML