choojs / nanomorph

🚅 - Hyper fast diffing algorithm for real DOM nodes

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[optimization] child node reordering

yoshuawuyts opened this issue · comments

commented

So @developit pointed out another optimization we could do is check node reordering. Often times lists get updated; items get appended or removed. Given that lists, forms and links are the staple elements of HTML this is probably worth optimizing for.

The new node comparison algorithm would then become:

  1. check if node is same, if yes do nothing
  2. compare the full array of child elements with equality checks, apply the re-order algorithm
  3. insert new children where reordering didn’t apply

I reckon this should speed up the reordering case by quite a bunch. Help welcome! ✨

commented

It seems like adding an ID check could also be a cheap optimization.

That's what keys do in react, some prior art.

Is there a way we can do children node reordering without having to force the user to supply a identifying key for every element?
So far every implementation I have seen has had this.

Problem is when the children look alike but children's-children contains the changing content. Apart from creating a Merkle tree or having the user supply a key I cannot see any other way of doing it.
Edit: I just saw the merkle tree idea was mentioned in issue #1 🤣

I would love to help with this. ✨

commented

@reminyborg well, each DOM element is just an object. If we set a property on the object we could use it as a unique identifier for the child nodes; removing the need for stuff to happen in userland. I think at least; hmmmm

@yoshuawuyts i might be wrong, in the cases of reordering with only the children's-children changing we might still end up mutating the elements instead of moving them. The effects are probably most apparent when dealing with focus.

A possible solution would be a optional "key" property. It would simplify reordering and keeping DOM state like focus intact. And if "key" is not used we can solve it optimistically.
I remember being discussed in mithril: http://mithril.js.org/mithril.html#dealing-with-focus

EDIT: I prefer morphdom's solution: Adding an ID id check on the elements as @kristoferjoseph suggested, requiring that works great for the edge cases where we want to keep a sub tree's DOM state intact (focus, input values, etc.) when reordering. Should also be suggested for optimising complex lists.

Its not apparent from morphdoms docs. We should add this to the readme when the feature ready.

commented

+1 on ID check
Also Merkle rees is such and interesting idea we should def add an experimental branch that tries this out.

commented
commented

I was thinking about what to name this prop, and key seems like it's the most used:

  • react has <div key=${number}></div>
  • vue has <div key=${number}></div>
  • inferno has <div key=${number}></div>

Also from infernojs/inferno#406:

Unlike React, Inferno promotes correct usage of instances where you might want, and not want, keys. Using either in different situations can have different performance characteristics depending on what is being "patched". Inferno could warn for not using keys on lists that contain stateful DOM elements that can have big issues if not keyed (e.g. inputs, selects, iframes, video, audio etc) – but that's not in scope for now. It might be a good task to add to the issue backlog though.

This made me realize that if we want to prevent onload / unonload from going haywire in lists we need to implement reordering for sure.

commented

blep, ok I think we might need to rename it to <div data-key=${number}></div>; was just thinking about it and figured using the data- prefix might actually be a bit nicer (:

I was experimenting with the thought that using the element ID would be enough.

I was reading morphdoms reasonings about it in patrick-steele-idem/morphdom#3

It would require users to create document unique ID's when needing list performance or preserving DOM state. But it would also ensure that the 'children' could survive being moved to another place in the tree.

commented

@reminyborg ah this explains a lot - I've run into issues before where I set an id on an element and it would stop updating. Didn't know why, just considered it a morphdom gotcha.

I'm not necessarily a fan of using IDs as keys, because IDs meant to be unique per page which means there could be conflicts. Using a data- attributed on the other hand feels less intrusive imo and only serves as a hint to the algorithm on how top optimize stuff

commented

actually IDs might make sense - if we document it well tho ✨ - less new words to invent

I've found in working on an infinite list module (https://github.com/editdata/infinite-elements) that having an id or key for reordering is essential for making it work.

Using the id by default and providing the option of using an alternate key attribute might be good. morphdom has a getNodeKey option to accomplish this.

commented
commented

From IRC:

17:04 <yoshuawuyts> how about: look over list of elements, reference each child by an id in an object, reference new children by id in object, morph nodes with same id, then loop over nodes and skip the ones that have already been morphed (e.g. do the reordering part)
17:05 <julian> yeah that sounds good, but doesn't solve the on-load problem, right?
17:06 <yoshuawuyts> it would because we never remove them from the dom
17:06 <julian> how do you reorder AB to BA without removing a node?
17:06 <yoshuawuyts> oh fuck lol
17:07 <julian> maybe on-load needs to be changed to allow this
17:07 <julian> yes i think so
17:07 <julian> it should after deletion check if "the same element" doesn't exist after all, maybe like that?
17:08 <yoshuawuyts> dang, reordering lists is tricky. appending / prepending nodes is the easier case
17:08 <julian> yeah definitely
17:09 <julian> deleting and adding in between works too
17:09 <julian> just reordering
17:09 <yoshuawuyts> luckily onload is a different concern - if we solve the algo in nanomorph then we can figure out how to best do stuff with onload on top of that
17:09 <julian> ok, cool!
17:09 <yoshuawuyts> I think at least haha
17:09 <julian> it "has to be that way"
17:09 <julian> not much we can do in nanomorph otherwise
17:10 <yoshuawuyts> yeahh
17:10 <julian> on-load should work with the concept of moving elements around
17:10 <julian> that should be a basic premise
17:10 <yoshuawuyts> we could so something like: listen for onload events -> nextTick -> check if currently in DOM -> if not in DOM fire the onunload handler
17:11 <yoshuawuyts> which would allow for detaching -> immediately reattaching DOM nodes
17:11 <julian> yeah i was thinking the same
17:11 <yoshuawuyts> woot!
commented

Patch to fix this was merged, but current master shows a regression in certain cases:

screen shot 2017-05-10 at 11 27 16

This was from my CSSconf talk slides, when upgrading to new nanomorph things failed. Oooops.

https://github.com/yoshuawuyts/css-conf-talk/tree/master

Wondering how this could affect reordered components that return a proxy.

commented

Fantastic. I suspected so.

commented

choojs/nanohtml#74 fixed the regression I encountered - haven't been able to reproduce. We should stay on the lookout for possible regressions tho

commented

v5.0.0

commented

This is going to be shipped in choo v6, requires #71 to land