> Time slicing keeps React responsive while it runs your code. Your code isn’t just DOM updates or “diffing”. It’s any JS logic you do in your components! Sometimes you gotta calculate things. No framework can magically speed up arbitrary code.
In my experience, as your app grows, the amount of time you spend on dom reconciliation becomes negligible compared to your own business logic. In this case, having a framework like React (especially with concurrent mode) will really help improve perceived user experience over a naive compiled implementation.
> In my experience, as your app grows, the amount of time you spend on dom reconciliation becomes negligible compared to your own business logic. In this case, having a framework like React (especially with concurrent mode) will really help improve perceived user experience over a naive compiled implementation.
In my experience, the exact opposite occurs. If there is ever any heavy computation I need to do, I usually try spawn a web worker or offload it to the server. In contrast, as your app tree grows reconciliation costs grow (super?)linearly, and more importantly there is (currently) no way to offload reconciliation.
Same, the only time I've run into performance issues with Vue after building many very complex deeply nested components prior to this was one which ground to a halt on re-rendering because I was simply rendering too many elements into the DOM with their subsequent watchers filling up memory.
After hours of combing through frames of the memory profiler and seeing only highly concurrent framework calls the only solution was to paginate the particular content. 99% of the users never had this issue but it was 1-2 customers who had thousands of components to render instead of the usual hundreds.
I'm really curious now if Svelte would have helped with that because it was a huge dev timesink and one where I was never satisfied with the solution. As it really should be able to render that amount of data. It obviously wasn't a problem in the jQuery/Rails version I was replacing and improving upon (although page load times was higher).
The new React concurrency model wouldn't have helped from what I've read. I just needed something lighter weight from the rendering model itself. Vue 3.0 is apparently going to come with plenty of performance improvements so I'm looking forward to that as well.
> After hours of combing through frames of the memory profiler and seeing only highly concurrent framework calls the only solution was to paginate the particular content. 99% of the users never had this issue but it was 1-2 customers who had thousands of components to render instead of the usual hundreds.
Did you try something like https://github.com/Akryum/vue-virtual-scroller? The trick is if you know the height/width of the elements, you can only render the elements directly in the viewport (+ some padding) and replace the missing elements with fixed-size blank divs, whose width and height you can find with some math. That way, you don't have to rely on the browser to layout your elements, nor do you have to reconcile hidden elements. (Essentially, element occlusion culling for the virtual DOM.)
Looks like vue-virtual-scroller only works with fixed-height elements (because n * m is easier to compute than n_1 + ... + n_m), but as long as you don't rely on the browser for layout the same trick works with preknown variable element sizes.
That wouldn't have worked for the problem unfortunately. It was a pretty compact UI with a lot going on so scrolls wouldn't have masked enough components. Thanks for the link though.
You could use svelte just for that heavy component and see if it makes a difference? Svelte compiled output is very small (only brings what you need) so you can quite easily embed it without dragging along a whole extra framework.
I think it neglects Dan's original point. Say you're doing a search input that filters a list of elements using fuzzy matching. No amount of optimization in your components or the framework is going to make the fuzzy string matching library you use work faster. Faster renders might make more room for the main thread to update, but fundamentally the problem persists. Concurrent React would allow you to type while the fuzzy matching happens asynchronously.
This is different than the demo shown. The demo with the charts is very render heavy. It's an unrealistic experience (and in my opinion, it is regretful that it was used to show the power of async rendering). In any real application, if you're rerendering thousands of nodes on every key press when their component instances are not changing input/state, something is very wrong. A fuzzy string matching filter is a much better example of this, since the visibility of each item is dependent on the state of the text input.
Sure, not all items in your list will update simultaneously. But are they really all visible on screen? Do they really all do need to be updating instantly? The overhead of the framework is almost certainly negligible here either way. But the scheduling that takes place is going to be critical, because that's what will directly affect how the app's performance is perceived by the user.
> Say you're doing a search input that filters a list of elements using fuzzy matching. No amount of optimization in your components or the framework is going to make the fuzzy string matching library you use work faster.
> Concurrent React would allow you to type while the fuzzy matching happens asynchronously.
Are you sure that is the case?
From what i gathered about concurrent React and Fiber is that it can split the rendering of multiple components into different time slices, and also give more priority to certain events, so that some re-renders are kept smooth and responsive (the input typing on the demo), while others can be split into multiple frames and delayed a bit (the update on the carts).
But all React can do is to split up multiple render() calls (or function component calls) into different time slices. It cannot "slice" a single render() call any further. So, if there's an expensive synchronous computation inside that render(), like the hypothetical `fuzzySearch(query)`, that computation will block the main thread no matter what, and the application will be unresponsive until that computation finishes.
There's no going around that if the fuzzy match function is synchronous. That function would have to be changed not to block the main thread. E.g., it could have a timeout so it doesn't take longer than, say, 10ms, and return a partial list of results on that case. Or be a generator, so you can consume its matches one by one and split that work into different frames. Or you could move that computation to a webworker, thus making it effectively asynchronous, and avoiding blocking the main thread.
I don't think there's much React, or any framework, can magically do in this case to make a synchronous expensive operation not block the app. But maybe i'm completely wrong and that's exactly what concurrent React does; or maybe i misunderstood your scenario entirely :)
Instead of results.filter(fuzzySearch(query).match) at the list level, you'd simply map all results and do the fuzzy matching within each result's render function (returning null if it doesn't match).
In that case, instead of fuzzy matching each result in one render, it's spread over many smaller renders. When those get executed is much less important and can be scheduled for idle time by the browser.
That's actually what I didn't understand in Dan Abramov's response ...
If you run a synchronous function that takes 2 seconds your app will block whether you use Svelte or React or whatever. You need to offload it to a webworker anyway.
Still I think it's a good idea to update the DOM not more often than needed: Only update the last change of an element every 23 ms and skip the changes that have been overriden. You can do this without a virt DOM.
> Time slicing keeps React responsive while it runs your code. Your code isn’t just DOM updates or “diffing”. It’s any JS logic you do in your components! Sometimes you gotta calculate things. No framework can magically speed up arbitrary code.
In my experience, as your app grows, the amount of time you spend on dom reconciliation becomes negligible compared to your own business logic. In this case, having a framework like React (especially with concurrent mode) will really help improve perceived user experience over a naive compiled implementation.