5 ms·
The article says that items = items.filter(i => i !== item); is compiled to $$invalidate('items', items = items.filter(i => i !== item)); So this in
by daoxid 7y ago
The article says that
items = items.filter(i => i !== item);
is compiled to
$$invalidate('items', items = items.filter(i => i !== item));
So this invalidates the whole array, right? Would this then re-render the whole array, i.e., remove and recreate all DOM nodes? And if so, does Svelte support more fine-grained ways to update arrays?
- hajile 7y agoThe TL;DR is that Svelte overpromises. They can't possibly write code for every transformation combination as code size would grow exponentially (I'm not completely sure, but I think predicting transforms would involve the halting problem). (thanks to whoever bothered to do this writeup). https://github.com/gactjs/gact/blob/master/docs/long-live-the-virtual-dom.md https://github.com/gactjs/gact/blob/master/docs/long-live-th... EDIT: if anyone has proof this is not true, I'd love to hear their counterargument.
- daoxid 7y agoI guess whether a structural comparison (VDOM) or a value comparison (Svelte) is more efficient really depends on the concrete use-case. For example, even changing a single value in a big list requires the whole VDOM to be recreated, but should be very efficient in Svelte as it can directly modify the specific DOM node. On the other hand, as pointed out in the article you linked, if changing a value affects a large part of the DOM, but does not actually change that much, then value-comparing frameworks (e.g. Svelte) probably do a lot of unnecessary work compared to VDOM-based approaches (intuitively I would think that this case does not occur that often).
- hajile 7y agoYou don't have to recreate the entire vdom if only one part changes. You need only recreate that component and its children (something like React's PureComponent or shouldComponentUpdate optimizations can prevent the worst cases without too much trouble). Changing branches in a component is extremely common in code I write (very large business application with lots of rules). Another important optimization is caching and re-using DOM nodes. With a vdom, you know exactly which properties and attributes differ from the default node. To reuse a node you need only update these properties to their new values or restore their original values. Without that tracking, it would be computationally cheaper to just create a new node. One important example of this is our use of flyweight scroller patterns in lists. The content of the sub-tree changes, but most of the sub-components stay the same, so a vdom could keep most of the dom nodes around.
- briantakita 7y ago> Each second Svelte will completely destroy the tree from the previous second, and completely rebuild the tree for this second Here is the repl of the code you posted. https://svelte.dev/repl/1a70f2f38af94ed7ac8bd032feea52f9?version=3.8.1 https://svelte.dev/repl/1a70f2f38af94ed7ac8bd032feea52f9?ver... A Svelte component is mutable & manages the related DOM elements which are also mutable. In the conditional, the tree is re-rendered. Would React's diff algorithm be able to recycle the `<p>surgical</p>` DOM Node? How much more performant would detaching & reattaching `<p>surgical</p>` be than recreating `<p>surgical</p>`? Assuming the `<p>surgical</p>` can be recycled, the use case having the largest effect is a large inner tree being wrapped/unwrapped; where the inner tree would simply be moved instead of recreated. In Svelte, this can be optimized by using a subcomponent, as seen in the repl example. You can examine the compiled js.
- hajile 7y agoI believe React recycles DOM nodes based on type (they decide some DOM nodes are faster to recreate instead of reuse depending on the circumstances) I may be wrong, but I believe hyperapp recycles both the physical DOM node and the attached vdom node together (IIRC, they mark reused vdom nodes as "recycled" instead of directly comparing objects so they don't have to construct as many new vdom objects). Preact kinda cheats here because they diff against the DOM directly.
- deleted 7y ago[deleted]
- rich_harris 7y agoSvelte author here. That post overlooks a number of important points; I've responded here https://www.reddit.com/r/javascript/comments/ckpdxk/long_live_the_virtual_dom/evq8g2k/ https://www.reddit.com/r/javascript/comments/ckpdxk/long_liv...
- hajile 7y agoReact is very far from the fastest vdom. It notably suffers from needing to work with non-DOM back-ends where a particular heuristic that is good in the DOM may either be useless or (worse) actively degrade performance. Preact, Snabbdom, or Inferno would probably be better points of comparison to Svelte's approach as they are much more optimized for the web. DOM nodes can actually be recycled relatively easily. Cache the nodes by type then by class. Most recycled nodes would be a 100% match based on those two alone. If type and class match, then you have a super-high probability of dealing with old nodes for the same object, so few modifications are required. Most nodes without classes tend to have no attributes changed (<div>, <p>, etc) so they will match as well. Store those nodes with their vdom attached and you will have a record of what has been modified so patching is fast. This optimization exists in some vdom implementations and would make the case in question much faster. There also seems to be an implication that the diffing algorithm will bloat. If you look at React, the diffing algorithm is a very small part of the codebase. This is hardly just an academic optimization though. It is the key to reducing overhead on long lists. With long lists of complex objects, you cannot rely on patching text values because there will undoubtedly be actual DOM differences. Rather than dooming the entire list to poor performance, you can recycle those nodes and retain most of the performance of a flyweight scroller where nodes are all identical. https://developers.google.com/web/updates/2016/07/infinite-scroller https://developers.google.com/web/updates/2016/07/infinite-s...
- daliusd 7y agoEither I have completely misunderstood you or you are simply wrong. It looks like you are implying that vdom is magical solution to long lists and svelte should have problem here. While in real world we have this: https://svelte.dev/repl/f78ddd84a1a540a9a40512df39ef751b?version=3.8.1 https://svelte.dev/repl/f78ddd84a1a540a9a40512df39ef751b?ver...
- gactleaks 7y agoYou’re welcome :) There’s more discussion here:https://mobile.twitter.com/Shub7241/status/1157004907775324160 https://mobile.twitter.com/Shub7241/status/11570049077753241....
- hajile 7y agoWhen discussing structural changes, it seems like lifting constant fragments a compile time wasn't discussed. There's a Babel React optimizer to do exactly this. Lift those to their own functions. Since those functions don't take any props or state, their values are cached almost indefinitely.
- deleted 7y ago[deleted]
- gactleaks 7y agoA generalisation of that optimisation is discussed here: https://twitter.com/MateuszOkon/status/1159222281874366464 https://twitter.com/MateuszOkon/status/1159222281874366464
- Glench 7y agoTo actually answer your question, no, $$invalidate doesn't mean all the DOM nodes get recreated. You can see that in this example: https://svelte.dev/repl/d3898004792c467d8eacefb054d8263a?version=3.8.1 https://svelte.dev/repl/d3898004792c467d8eacefb054d8263a?ver... Also, Svelte has keys like React which can help out. See this example in the tutorial for that: https://svelte.dev/tutorial/keyed-each-blocks https://svelte.dev/tutorial/keyed-each-blocks
- daoxid 7y agoThanks! For some reason it didn't occur to me that Svelte could just compare the new array to the old array (if this is how it's implemented).
- rich_harris 7y agoSvelte author here. There's a couple of things to note: - as Glench mentioned, it's not destroying and recreating stuff unnecessarily. By default it will create or destroy blocks at the end of the `each`, if the length of the array has changed. If you use a `key` then it will diff the input (as opposed to the output) and move elements around accordingly. - `$$invalidate` is just an implementation detail, and is subject to change. There a couple of directions in which we plan to do so. One is to use bitmask-based change tracking, which would allow us to generate more compact code resulting in faster change checks (e.g. `changed & 7` instead of `changed.foo || changed.bar || changed.baz` when updating the view). Another is to track which nested properties of an object or array have changed — at the moment, `items[i] = item` invalidates all of `items`, but it would be great if we had a way to invalidate `items[i]` instead.