Created
August 10, 2017 06:12
-
-
Save leeoniya/7622890577f1bc36eee70da368f69dd7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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