-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bugfix/real incremental diff based rendering #411
Merged
AndyOGo
merged 22 commits into
develop
from
bugfix/real-incremental-diff-based-rendering
May 1, 2018
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
74c5a60
npm i nanomorph
AndyOGo ea8479b
start adding incremental updates - firefox svg still broken
AndyOGo 089b93b
fixed possible memory leak
AndyOGo e312d7f
fixed typo
AndyOGo 81bfa9d
if node are live dont move them to fraqment
AndyOGo 8a00844
added doclet
AndyOGo 3601a23
improved isSameNode once
AndyOGo fe10811
only return childnodes of component for morphing of current component
AndyOGo 9ab206f
only patch childNodes if necessary
AndyOGo 4b4d187
fixed broken stroke
AndyOGo 72cfcdd
fixed broken light DOM
AndyOGo 1ffbbf8
added todo
AndyOGo 5355f39
Fix dependency
e30a92c
fixed wrong param order
AndyOGo f8f332c
clearify notInitial
AndyOGo e9ff7a9
prefixed with state
AndyOGo 2bb6366
ooopps forgot to nigate
AndyOGo a1cfb44
copied nanomorph temporarely
AndyOGo dc98ba8
fixed ot component scoped
AndyOGo 28981a5
removed nanomorph
AndyOGo 52e3cc1
fixed wrong binary op
AndyOGo 2291fb0
clean up is same node
AndyOGo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import morph from './morph'; | ||
|
||
const TEXT_NODE = 3; | ||
// var DEBUG = false | ||
|
||
export default componentMorph; | ||
|
||
// Morph one tree into another tree | ||
// | ||
// no parent | ||
// -> same: diff and walk children | ||
// -> not same: replace and return | ||
// old node doesn't exist | ||
// -> insert new node | ||
// new node doesn't exist | ||
// -> delete old node | ||
// nodes are not the same | ||
// -> diff nodes and apply patch to old node | ||
// nodes are the same | ||
// -> walk all child nodes and append to old node | ||
function componentMorph(oldTree, newTree) { | ||
// if (DEBUG) { | ||
// console.log( | ||
// 'componentMorph\nold\n %s\nnew\n %s', | ||
// oldTree && oldTree.outerHTML, | ||
// newTree && newTree.outerHTML | ||
// ) | ||
// } | ||
if (typeof oldTree !== 'object') { | ||
throw new Error('componentMorph: oldTree should be an object'); | ||
} | ||
|
||
if (typeof newTree !== 'object') { | ||
throw new Error('componentMorph: newTree should be an object'); | ||
} | ||
|
||
const tree = walk(newTree, oldTree); | ||
// if (DEBUG) console.log('=> morphed\n %s', tree.outerHTML) | ||
return tree; | ||
} | ||
|
||
// Walk and morph a dom tree | ||
function walk(newNode, oldNode) { | ||
// if (DEBUG) { | ||
// console.log( | ||
// 'walk\nold\n %s\nnew\n %s', | ||
// oldNode && oldNode.outerHTML, | ||
// newNode && newNode.outerHTML | ||
// ) | ||
// } | ||
if (!oldNode) { | ||
return newNode; | ||
} else if (!newNode) { | ||
return null; | ||
} else if (newNode.isSameNode && newNode.isSameNode(oldNode)) { | ||
return oldNode; | ||
} else if (newNode.tagName !== oldNode.tagName) { | ||
return newNode; | ||
} | ||
|
||
morph(newNode, oldNode); | ||
|
||
if (!oldNode.skipChildren || !oldNode.skipChildren()) { | ||
updateChildren(newNode, oldNode); | ||
} | ||
|
||
return oldNode; | ||
} | ||
|
||
// Update the children of elements | ||
// (obj, obj) -> null | ||
function updateChildren(newNode, oldNode) { | ||
// if (DEBUG) { | ||
// console.log( | ||
// 'updateChildren\nold\n %s\nnew\n %s', | ||
// oldNode && oldNode.outerHTML, | ||
// newNode && newNode.outerHTML | ||
// ) | ||
// } | ||
let oldChild; | ||
let newChild; | ||
let morphed; | ||
let oldMatch; | ||
|
||
// The offset is only ever increased, and used for [i - offset] in the loop | ||
let offset = 0; | ||
|
||
/* eslint-disable no-plusplus */ | ||
for (let i = 0; ; i++) { | ||
oldChild = oldNode.childNodes[i]; | ||
newChild = newNode.childNodes[i - offset]; | ||
// if (DEBUG) { | ||
// console.log( | ||
// '===\n- old\n %s\n- new\n %s', | ||
// oldChild && oldChild.outerHTML, | ||
// newChild && newChild.outerHTML | ||
// ) | ||
// } | ||
// Both nodes are empty, do nothing | ||
if (!oldChild && !newChild) { | ||
break; | ||
|
||
// There is no new child, remove old | ||
} else if (!newChild) { | ||
oldNode.removeChild(oldChild); | ||
i--; | ||
|
||
// There is no old child, add new | ||
} else if (!oldChild) { | ||
oldNode.appendChild(newChild); | ||
offset++; | ||
|
||
// Both nodes are the same, morph | ||
} else if (same(newChild, oldChild)) { | ||
morphed = walk(newChild, oldChild); | ||
if (morphed !== oldChild) { | ||
oldNode.replaceChild(morphed, oldChild); | ||
offset++; | ||
} | ||
|
||
// Both nodes do not share an ID or a placeholder, try reorder | ||
} else { | ||
oldMatch = null; | ||
|
||
// Try and find a similar node somewhere in the tree | ||
for (let j = i; j < oldNode.childNodes.length; j++) { | ||
if (same(oldNode.childNodes[j], newChild)) { | ||
oldMatch = oldNode.childNodes[j]; | ||
break; | ||
} | ||
} | ||
|
||
// If there was a node with the same ID or placeholder in the old list | ||
if (oldMatch) { | ||
morphed = walk(newChild, oldMatch); | ||
if (morphed !== oldMatch) { | ||
offset++; | ||
} | ||
|
||
oldNode.insertBefore(morphed, oldChild); | ||
|
||
// It's safe to morph two nodes in-place if neither has an ID | ||
} else if (!newChild.id && !oldChild.id) { | ||
morphed = walk(newChild, oldChild); | ||
if (morphed !== oldChild) { | ||
oldNode.replaceChild(morphed, oldChild); | ||
offset++; | ||
} | ||
|
||
// Insert the node at the index if we couldn't morph or find a matching node | ||
} else { | ||
oldNode.insertBefore(newChild, oldChild); | ||
offset++; | ||
} | ||
} | ||
} | ||
} | ||
|
||
function same(a, b) { | ||
if (a.id) { | ||
return a.id === b.id; | ||
} | ||
if (a.isSameNode) { | ||
return a.isSameNode(b); | ||
} | ||
if (a.tagName !== b.tagName) { | ||
return false; | ||
} | ||
if (a.type === TEXT_NODE) { | ||
return a.nodeValue === b.nodeValue; | ||
} | ||
return false; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
let sameNodeCache = []; | ||
|
||
/** | ||
* Make sure that another piece of code is/can managing that part of the DOM tree. | ||
* | ||
* @link https://github.com/choojs/nanomorph#caching-dom-elements | ||
* @param node | ||
*/ | ||
export function isSameNodeOnce(node) { | ||
node.isSameNode = isSameNodeStopMorph; | ||
|
||
sameNodeCache.push(node); | ||
|
||
function isSameNodeStopMorph() { | ||
return true; | ||
} | ||
} | ||
|
||
/** | ||
* Make sure to clear overwritten `isSameNode` API after DOM diffing. | ||
*/ | ||
export function clearIsSameNode() { | ||
let node; | ||
|
||
// eslint-disable-next-line no-cond-assign | ||
while (node = sameNodeCache.pop()) { | ||
delete node.isSameNode; | ||
} | ||
|
||
sameNodeCache = []; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we may will create it's own npm package for morphin in the future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a todo in the file directly and also WHY we did copy/modify the nanomorph one