Last active
September 4, 2018 07:46
-
-
Save fuunnx/24dc7745e4d71111e4f83eb2de6255ab to your computer and use it in GitHub Desktop.
Snabbdom - Shallow copy duplicated vnode on DOME node creation
This file contains hidden or 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
```sh | |
mkdir patches | |
mv snabbdom+0.7.0.patch patches/snabbdom+0.7.0.patch | |
npm install [email protected] [email protected] && ./node_modules/.bin/patch-package | |
``` |
This file contains hidden or 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
patch-package | |
--- a/node_modules/snabbdom/snabbdom.js | |
+++ b/node_modules/snabbdom/snabbdom.js | |
@@ -88,7 +88,8 @@ function init(modules, domApi) { | |
for (i = 0; i < children.length; ++i) { | |
var ch = children[i]; | |
if (ch != null) { | |
- api.appendChild(elm, createElm(ch, insertedVnodeQueue)); | |
+ children[i] = shallowCopy(children[i]) | |
+ api.appendChild(elm, createElm(children[i], insertedVnodeQueue)); | |
} | |
} | |
} | |
@@ -112,7 +113,21 @@ function init(modules, domApi) { | |
for (; startIdx <= endIdx; ++startIdx) { | |
var ch = vnodes[startIdx]; | |
if (ch != null) { | |
- api.insertBefore(parentElm, createElm(ch, insertedVnodeQueue), before); | |
+ vnodes[startIdx] = shallowCopy(vnodes[startIdx]) | |
+ try { | |
+ api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before); | |
+ } catch (e) { | |
+ console.log({parentElm, before, child: vnodes[startIdx]}) | |
+ console.error(e) | |
+ | |
+ console.log(elms(tree)) | |
+ | |
+ function elms(node) { | |
+ return node && [node.elm, (node.children || []).map(elms)] | |
+ } | |
+ debugger | |
+ // throw e | |
+ } | |
} | |
} | |
} | |
@@ -151,7 +166,12 @@ function init(modules, domApi) { | |
} | |
} | |
else { | |
+ try { | |
api.removeChild(parentElm, ch.elm); | |
+ } catch (e) { | |
+ console.log({parentElm, elm: ch.elm, ch}) | |
+ console.error(e) | |
+ } | |
} | |
} | |
} | |
@@ -209,13 +229,15 @@ function init(modules, domApi) { | |
} | |
idxInOld = oldKeyToIdx[newStartVnode.key]; | |
if (isUndef(idxInOld)) { | |
- api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm); | |
+ newCh[newStartIdx] = shallowCopy(newCh[newStartIdx]) | |
+ api.insertBefore(parentElm, createElm(newCh[newStartIdx], insertedVnodeQueue), oldStartVnode.elm); | |
newStartVnode = newCh[++newStartIdx]; | |
} | |
else { | |
elmToMove = oldCh[idxInOld]; | |
if (elmToMove.sel !== newStartVnode.sel) { | |
- api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm); | |
+ newCh[newStartIdx] = shallowCopy(newCh[newStartIdx]) | |
+ api.insertBefore(parentElm, createElm(newCh[newStartIdx], insertedVnodeQueue), oldStartVnode.elm); | |
} | |
else { | |
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); | |
@@ -228,6 +250,9 @@ function init(modules, domApi) { | |
} | |
if (oldStartIdx > oldEndIdx) { | |
before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm; | |
+ if(before && before.parentNode !== parentElm) { | |
+ before = parentElm.childNodes[newEndIdx] || null | |
+ } | |
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue); | |
} | |
else if (newStartIdx > newEndIdx) { | |
@@ -303,5 +328,15 @@ function init(modules, domApi) { | |
return vnode; | |
}; | |
} | |
+ | |
+function shallowCopy(node) { | |
+ if(!node || !node.elm) {return node;} | |
+ return { | |
+ ...node, | |
+ elm: undefined, | |
+ children: node.children && node.children.slice(), | |
+ } | |
+} | |
+ | |
exports.init = init; | |
//# sourceMappingURL=snabbdom.js.map |
This file contains hidden or 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
"use strict"; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
var vnode_1 = require("./vnode"); | |
var is = require("./is"); | |
var htmldomapi_1 = require("./htmldomapi"); | |
function isUndef(s) { return s === undefined; } | |
function isDef(s) { return s !== undefined; } | |
var emptyNode = vnode_1.default('', {}, [], undefined, undefined); | |
function sameVnode(vnode1, vnode2) { | |
return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel; | |
} | |
function isVnode(vnode) { | |
return vnode.sel !== undefined; | |
} | |
function createKeyToOldIdx(children, beginIdx, endIdx) { | |
var i, map = {}, key, ch; | |
for (i = beginIdx; i <= endIdx; ++i) { | |
ch = children[i]; | |
if (ch != null) { | |
key = ch.key; | |
if (key !== undefined) | |
map[key] = i; | |
} | |
} | |
return map; | |
} | |
var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post']; | |
var h_1 = require("./h"); | |
exports.h = h_1.h; | |
var thunk_1 = require("./thunk"); | |
exports.thunk = thunk_1.thunk; | |
function init(modules, domApi) { | |
var i, j, cbs = {}; | |
var api = domApi !== undefined ? domApi : htmldomapi_1.default; | |
for (i = 0; i < hooks.length; ++i) { | |
cbs[hooks[i]] = []; | |
for (j = 0; j < modules.length; ++j) { | |
var hook = modules[j][hooks[i]]; | |
if (hook !== undefined) { | |
cbs[hooks[i]].push(hook); | |
} | |
} | |
} | |
function emptyNodeAt(elm) { | |
var id = elm.id ? '#' + elm.id : ''; | |
var c = elm.className ? '.' + elm.className.split(' ').join('.') : ''; | |
return vnode_1.default(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm); | |
} | |
function createRmCb(childElm, listeners) { | |
return function rmCb() { | |
if (--listeners === 0) { | |
var parent_1 = api.parentNode(childElm); | |
api.removeChild(parent_1, childElm); | |
} | |
}; | |
} | |
function createElm(vnode, insertedVnodeQueue) { | |
var i, data = vnode.data; | |
if (data !== undefined) { | |
if (isDef(i = data.hook) && isDef(i = i.init)) { | |
i(vnode); | |
data = vnode.data; | |
} | |
} | |
var children = vnode.children, sel = vnode.sel; | |
if (sel === '!') { | |
if (isUndef(vnode.text)) { | |
vnode.text = ''; | |
} | |
vnode.elm = api.createComment(vnode.text); | |
} | |
else if (sel !== undefined) { | |
// Parse selector | |
var hashIdx = sel.indexOf('#'); | |
var dotIdx = sel.indexOf('.', hashIdx); | |
var hash = hashIdx > 0 ? hashIdx : sel.length; | |
var dot = dotIdx > 0 ? dotIdx : sel.length; | |
var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel; | |
var elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag) | |
: api.createElement(tag); | |
if (hash < dot) | |
elm.setAttribute('id', sel.slice(hash + 1, dot)); | |
if (dotIdx > 0) | |
elm.setAttribute('class', sel.slice(dot + 1).replace(/\./g, ' ')); | |
for (i = 0; i < cbs.create.length; ++i) | |
cbs.create[i](emptyNode, vnode); | |
if (is.array(children)) { | |
for (i = 0; i < children.length; ++i) { | |
var ch = children[i]; | |
if (ch != null) { | |
children[i] = shallowCopy(children[i]) | |
api.appendChild(elm, createElm(children[i], insertedVnodeQueue)); | |
} | |
} | |
} | |
else if (is.primitive(vnode.text)) { | |
api.appendChild(elm, api.createTextNode(vnode.text)); | |
} | |
i = vnode.data.hook; // Reuse variable | |
if (isDef(i)) { | |
if (i.create) | |
i.create(emptyNode, vnode); | |
if (i.insert) | |
insertedVnodeQueue.push(vnode); | |
} | |
} | |
else { | |
vnode.elm = api.createTextNode(vnode.text); | |
} | |
return vnode.elm; | |
} | |
function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) { | |
for (; startIdx <= endIdx; ++startIdx) { | |
var ch = vnodes[startIdx]; | |
if (ch != null) { | |
vnodes[startIdx] = shallowCopy(vnodes[startIdx]) | |
try { | |
api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before); | |
} catch (e) { | |
console.log({parentElm, before, child: vnodes[startIdx]}) | |
console.error(e) | |
console.log(elms(tree)) | |
function elms(node) { | |
return node && [node.elm, (node.children || []).map(elms)] | |
} | |
debugger | |
// throw e | |
} | |
} | |
} | |
} | |
function invokeDestroyHook(vnode) { | |
var i, j, data = vnode.data; | |
if (data !== undefined) { | |
if (isDef(i = data.hook) && isDef(i = i.destroy)) | |
i(vnode); | |
for (i = 0; i < cbs.destroy.length; ++i) | |
cbs.destroy[i](vnode); | |
if (vnode.children !== undefined) { | |
for (j = 0; j < vnode.children.length; ++j) { | |
i = vnode.children[j]; | |
if (i != null && typeof i !== "string") { | |
invokeDestroyHook(i); | |
} | |
} | |
} | |
} | |
} | |
function removeVnodes(parentElm, vnodes, startIdx, endIdx) { | |
for (; startIdx <= endIdx; ++startIdx) { | |
var i_1 = void 0, listeners = void 0, rm = void 0, ch = vnodes[startIdx]; | |
if (ch != null) { | |
if (isDef(ch.sel)) { | |
invokeDestroyHook(ch); | |
listeners = cbs.remove.length + 1; | |
rm = createRmCb(ch.elm, listeners); | |
for (i_1 = 0; i_1 < cbs.remove.length; ++i_1) | |
cbs.remove[i_1](ch, rm); | |
if (isDef(i_1 = ch.data) && isDef(i_1 = i_1.hook) && isDef(i_1 = i_1.remove)) { | |
i_1(ch, rm); | |
} | |
else { | |
rm(); | |
} | |
} | |
else { | |
try { | |
api.removeChild(parentElm, ch.elm); | |
} catch (e) { | |
console.log({parentElm, elm: ch.elm, ch}) | |
console.error(e) | |
} | |
} | |
} | |
} | |
} | |
function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) { | |
var oldStartIdx = 0, newStartIdx = 0; | |
var oldEndIdx = oldCh.length - 1; | |
var oldStartVnode = oldCh[0]; | |
var oldEndVnode = oldCh[oldEndIdx]; | |
var newEndIdx = newCh.length - 1; | |
var newStartVnode = newCh[0]; | |
var newEndVnode = newCh[newEndIdx]; | |
var oldKeyToIdx; | |
var idxInOld; | |
var elmToMove; | |
var before; | |
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { | |
if (oldStartVnode == null) { | |
oldStartVnode = oldCh[++oldStartIdx]; // Vnode might have been moved left | |
} | |
else if (oldEndVnode == null) { | |
oldEndVnode = oldCh[--oldEndIdx]; | |
} | |
else if (newStartVnode == null) { | |
newStartVnode = newCh[++newStartIdx]; | |
} | |
else if (newEndVnode == null) { | |
newEndVnode = newCh[--newEndIdx]; | |
} | |
else if (sameVnode(oldStartVnode, newStartVnode)) { | |
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue); | |
oldStartVnode = oldCh[++oldStartIdx]; | |
newStartVnode = newCh[++newStartIdx]; | |
} | |
else if (sameVnode(oldEndVnode, newEndVnode)) { | |
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue); | |
oldEndVnode = oldCh[--oldEndIdx]; | |
newEndVnode = newCh[--newEndIdx]; | |
} | |
else if (sameVnode(oldStartVnode, newEndVnode)) { | |
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue); | |
api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm)); | |
oldStartVnode = oldCh[++oldStartIdx]; | |
newEndVnode = newCh[--newEndIdx]; | |
} | |
else if (sameVnode(oldEndVnode, newStartVnode)) { | |
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue); | |
api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); | |
oldEndVnode = oldCh[--oldEndIdx]; | |
newStartVnode = newCh[++newStartIdx]; | |
} | |
else { | |
if (oldKeyToIdx === undefined) { | |
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); | |
} | |
idxInOld = oldKeyToIdx[newStartVnode.key]; | |
if (isUndef(idxInOld)) { | |
newCh[newStartIdx] = shallowCopy(newCh[newStartIdx]) | |
api.insertBefore(parentElm, createElm(newCh[newStartIdx], insertedVnodeQueue), oldStartVnode.elm); | |
newStartVnode = newCh[++newStartIdx]; | |
} | |
else { | |
elmToMove = oldCh[idxInOld]; | |
if (elmToMove.sel !== newStartVnode.sel) { | |
newCh[newStartIdx] = shallowCopy(newCh[newStartIdx]) | |
api.insertBefore(parentElm, createElm(newCh[newStartIdx], insertedVnodeQueue), oldStartVnode.elm); | |
} | |
else { | |
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); | |
oldCh[idxInOld] = undefined; | |
api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm); | |
} | |
newStartVnode = newCh[++newStartIdx]; | |
} | |
} | |
} | |
if (oldStartIdx > oldEndIdx) { | |
before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm; | |
if(before && before.parentNode !== parentElm) { | |
before = parentElm.childNodes[newEndIdx] || null | |
} | |
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue); | |
} | |
else if (newStartIdx > newEndIdx) { | |
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); | |
} | |
} | |
function patchVnode(oldVnode, vnode, insertedVnodeQueue) { | |
var i, hook; | |
if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) { | |
i(oldVnode, vnode); | |
} | |
var elm = vnode.elm = oldVnode.elm; | |
var oldCh = oldVnode.children; | |
var ch = vnode.children; | |
if (oldVnode === vnode) | |
return; | |
if (vnode.data !== undefined) { | |
for (i = 0; i < cbs.update.length; ++i) | |
cbs.update[i](oldVnode, vnode); | |
i = vnode.data.hook; | |
if (isDef(i) && isDef(i = i.update)) | |
i(oldVnode, vnode); | |
} | |
if (isUndef(vnode.text)) { | |
if (isDef(oldCh) && isDef(ch)) { | |
if (oldCh !== ch) | |
updateChildren(elm, oldCh, ch, insertedVnodeQueue); | |
} | |
else if (isDef(ch)) { | |
if (isDef(oldVnode.text)) | |
api.setTextContent(elm, ''); | |
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); | |
} | |
else if (isDef(oldCh)) { | |
removeVnodes(elm, oldCh, 0, oldCh.length - 1); | |
} | |
else if (isDef(oldVnode.text)) { | |
api.setTextContent(elm, ''); | |
} | |
} | |
else if (oldVnode.text !== vnode.text) { | |
api.setTextContent(elm, vnode.text); | |
} | |
if (isDef(hook) && isDef(i = hook.postpatch)) { | |
i(oldVnode, vnode); | |
} | |
} | |
return function patch(oldVnode, vnode) { | |
var i, elm, parent; | |
var insertedVnodeQueue = []; | |
for (i = 0; i < cbs.pre.length; ++i) | |
cbs.pre[i](); | |
if (!isVnode(oldVnode)) { | |
oldVnode = emptyNodeAt(oldVnode); | |
} | |
if (sameVnode(oldVnode, vnode)) { | |
patchVnode(oldVnode, vnode, insertedVnodeQueue); | |
} | |
else { | |
elm = oldVnode.elm; | |
parent = api.parentNode(elm); | |
createElm(vnode, insertedVnodeQueue); | |
if (parent !== null) { | |
api.insertBefore(parent, vnode.elm, api.nextSibling(elm)); | |
removeVnodes(parent, [oldVnode], 0, 0); | |
} | |
} | |
for (i = 0; i < insertedVnodeQueue.length; ++i) { | |
insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]); | |
} | |
for (i = 0; i < cbs.post.length; ++i) | |
cbs.post[i](); | |
return vnode; | |
}; | |
} | |
function shallowCopy(node) { | |
if(!node || !node.elm) {return node;} | |
return { | |
...node, | |
elm: undefined, | |
children: node.children && node.children.slice(), | |
} | |
} | |
exports.init = init; | |
//# sourceMappingURL=snabbdom.js.map |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment