Skip to content

Instantly share code, notes, and snippets.

@leeoniya
Created August 10, 2017 06:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leeoniya/7622890577f1bc36eee70da368f69dd7 to your computer and use it in GitHub Desktop.
Save leeoniya/7622890577f1bc36eee70da368f69dd7 to your computer and use it in GitHub Desktop.
<!doctype html>
<html>
<head>
<script>
domvm = (function() { "use strict";
var doc = document;
var emptyObj = {};
var isArr = Array.isArray;
function VNode() {}
VNode.prototype = {
constructor: VNode,
el: null,
tag: null,
body: null,
idx: null,
parent: null,
};
function preProc(vnew, parent, idx) {
vnew.parent = parent;
vnew.idx = idx;
if (isArr(vnew.body)) {
for (var i = 0; i < vnew.body.length; i++)
preProc(vnew.body[i], vnew, i);
}
}
function createElement(tag) {
return doc.createElement(tag);
}
function nextSib(sib) {
return sib.nextSibling;
}
function prevSib(sib) {
return sib.previousSibling;
}
function removeChild(parEl, el) {
parEl.removeChild(el);
el._node = null; // helps GC reclaim old vnodes
}
function insertBefore(parEl, el, refEl) {
parEl.insertBefore(el, refEl);
}
function insertAfter(parEl, el, refEl) {
insertBefore(parEl, el, refEl ? nextSib(refEl) : null);
}
function hydrate(vnode) {
if (vnode.el == null) {
vnode.el = createElement(vnode.tag);
if (isArr(vnode.body)) {
for (var i = 0; i < vnode.body.length; i++)
insertBefore(vnode.el, hydrate(vnode.body[i]));
}
else
{ vnode.el.textContent = vnode.body; }
}
vnode.el._node = vnode;
return vnode.el;
}
function nextNode(node, body) {
return body[node.idx + 1];
}
function prevNode(node, body) {
return body[node.idx - 1];
}
function parentNode(node) {
return node.parent;
}
function syncChildren(node, donor) {
var parEl = node.el,
body = node.body,
lftNode = body[0],
lftSib = ((donor.body)[0] || emptyObj).el,
tmpSib,
lsNode;
while (1) {
if (lftSib) {
if ((lsNode = lftSib._node) == null || parentNode(lsNode) !== node) {
tmpSib = nextSib(lftSib);
removeChild(parEl, lftSib);
lftSib = tmpSib;
continue;
}
}
if (lftNode == null) // reached end
{ break; }
else if (lftNode.el == null) {
insertBefore(parEl, hydrate(lftNode), lftSib);
lftNode = nextNode(lftNode, body);
}
else if (lftNode.el === lftSib) {
lftNode = nextNode(lftNode, body);
lftSib = nextSib(lftSib);
}
}
}
function patch(vnode, donor) {
var el = vnode.el = donor.el;
el._node = vnode;
if (isArr(donor.body) && isArr(vnode.body))
{ patchChildren(vnode, donor); }
else
{ el.firstChild.nodeValue = vnode.body; }
}
function patchChildren(vnode, donor) {
var nbody = vnode.body,
nlen = nbody.length,
obody = donor.body,
olen = obody.length;
var donor2,
node2,
fromIdx = 0;
for (var i = 0; i < nlen; i++) {
var node2 = nbody[i];
if (donor2 = obody[fromIdx]) {
patch(node2, donor2);
if (donor2.idx === fromIdx) {
fromIdx++;
if (fromIdx === olen && nlen > olen) {
break;
}
}
}
}
syncChildren(vnode, donor);
}
function ViewModel(view) {
this.render = view();
}
ViewModel.prototype = {
constructor: ViewModel,
node: null,
render: null,
mount: function(el) {
var vm = this;
vm.redraw();
insertBefore(el, vm.node.el);
return vm;
},
redraw: function redrawSync() {
var vm = this;
var vold = vm.node;
var vnew = vm.render();
preProc(vnew);
if (vold)
{ patch(vnew, vold); }
else
{ hydrate(vnew); }
vm.node = vnew;
},
};
return {
createView: function(view) {
return new ViewModel(view);
},
defineElement: function(tag, body) {
var node = new VNode;
node.tag = tag;
node.body = body;
return node;
}
};
})();
</script>
</head>
<body>
<script>
var el = domvm.defineElement;
var list = [];
function ListView() {
return () =>
el("ul", list.map(item =>
el("li", item.val)
));
}
function randInt(min, max) {
return Math.floor(Math.random()*(max-min+1)+min);
}
function randColor() {
return '#'+Math.floor(Math.random()*16777215).toString(16);
}
function makeList(qty) {
var items = [];
while (qty--)
items.push({val: randColor()});
return items;
}
var iter = 600;
// leaks
setTimeout(function() {
var vm = domvm.createView(ListView).mount(document.body);
var i = 0;
var it = setInterval(function() {
list = makeList(randInt(1, 100));
vm.redraw();
if (iter-- == 0)
clearInterval(it);
}, 100);
}, 1000);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment