Rendering multiple (hundreds or even thousands) web-components - javascript

The problem I am facing is that rendering a lot of web-components is slow. Scripting takes around 1,5s and then another 3s for rendering (mostly Layout + Recalculate styles) for ~5k elements, I plan to put much more than that into the DOM. My script to prepare those Elements takes around 100-200ms, the rest comes from constructor and other callbacks.
For normal HTML Elements a perf gain can be achieved with documentFragment, where you basically prepare a batch of elements, and only when you're done you attach them to the DOM.
Unfortunately, each web-component will call its constructor and other callbacks like connectedCallback, attributeChangedCallback etc. When having a lot of such components it's not really optimal.
Is there a way to "prerender" web-components before inserting them into the DOM?
I've tried to put them inside template elements and clone the contents, but the constructor is still called for each instance of my-component. One thing that did improve the performance is putting content that is attached to the shadow DOM inside a template outside of component and cloning it instead of using this.attachShadow({ mode: 'open' }).innerHTML=..., but that's not enough.

Do you really need to render all ~5k elements at once? You will face performance problems rendering that many elements in the DOM independently of if you manage to pre-initialize the components in memory.
A common technique for this scenario is to use a technique called "windowing" or "lazy rendering": the idea is to render only a small subset of your components at any given time depending on what's on the user viewport.
After a very quick search, I didn't find a library for web-components that implements this, but for reference in React you have react-window and in Vue vue-windowing

Related

Confused about Virtual DOM

Virtual DOM/DOM tree state in steps
So I've stumbled upon this image recently and I'm a bit confused about part where Virtual DOM state is applied to the real DOM. So let's say we changed <div> to a <header> in our React code, this change would be applied to Virtual DOM and then whole subtree would be re-rendered. But in Vanilla JS I could do the very same thing and subtree would be re-rendered aswell? So do I understand correctly that Virtual DOM duty among other things is to abstract real DOM operations we would need to do?
Would correct/efficient HTML DOM manipulations have same effect as working with Virtual DOM in complex applications?
So do I understand correctly that Virtual DOM duty among other things is to abstract real DOM operations we would need to do?
Yes.
Would correct/efficient HTML DOM manipulations have same effect as working with Virtual DOM in complex applications?
Yes.
You're right. A virtual DOM ultimately translates to calls to the real DOM. Therefore, the virtual DOM can only ever be slower than optimal usage of the real DOM. If you're adding, removing, moving elements on the actual DOM in a way that actually represents what changes you're making, it will be efficient.
Then why does Virtual DOM exist?
People invented virtual DOM to abstract away "how things are changing" and instead think about "how things are currently." The architecture of things like React and Vue is basically that components have the code to declare the way that the DOM should currently look. Then the code in these frameworks does a big re-computation of how they think things should be, then it diffs it against how the components said things should be the last time it asked them, then it applies that diff to the real DOM.
So, virtual DOM is a way to ameliorate the inherent slowness of this design, under the belief that its benefits will outweigh the costs.
Sources
https://svelte.dev/blog/virtual-dom-is-pure-overhead
https://medium.com/#hayavuk/why-virtual-dom-is-slower-2d9b964b4c9e
I'm not sure I fully understand your questions but I'll try to answer as fully as I can.
React works in Virtual DOM, which means it has a copy of the real dom and whenever it detects a change, it sees what the difference is and only rerenders that component and it's children in the subtree, NOT the whole one.
The way React detects change is through props. If the props of a component has been changed, it rerenders the component.
One way you could evade unnecessary re-renders of the children is if you use PureComponents, ShouldComponentUpdate, or the new memo and memo hooks. (memoization).
Also, the one other difference between normal(JavaScript) DOM and virtual DOM is that Virtual DOM is made up of objects and normal DOM is made of arrays. So, in arrays, search time is O(n) and in objects, it's constant time O(1) and that's why it is way faster
I hope this will clarify your doubts about the virtual DOM without going too much in detail.
Real DOM
It updates slow.
Can directly update HTML.
Creates a new DOM if element updates.
Uses a lot of memory resource.
Virtual DOM
Updates faster and it's light-weight.
Can't directly update HTML.
Updates the JSX if element updates.
Increased performance as uses less memory

How to measure a large number of components in React Native without causing an error?

I'm trying to implement a generic Drag-and-Drop module that I may or may not release on NPM.
I took inspiration from an already existing DnD module which consisted of essentially two main components: Draggables and DropZones.
The Draggable components would enable any content that they enclosed to be dragged around, and DropZones were the components that would react to when a Draggable component was dragged in/out of them and/or dropped onto them.
The module's functionality was too basic for what I needed, so I set off on making my own.
Everything has been going fine except for one aspect: measuring the absolute position of a large number of components in order to find their corresponding bounding shapes for collision detection.
Currently, I've been using the View's measure method to measure the absolute position and size of my components, however, if you call this method too many times within a short period of time, React Native will throw this error: Warning: Please report: Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code and it lists the offending method as {"module":"UIManager","method":"measure"}.
If I make a basic for loop that repeatedly calls the measure method, I can get it up to just over 500 iterations before React Native complains (this number may be hardware-dependent; I'm not sure).
Also, the same problem happens when using the View's other measuring methods (e.g. measureInWindow, measureLayout).
What's even worse is that up until now, I have been calling the measure method in a View's onLayout callback, however, I recently discovered that onLayout does not get called when a style changes that doesn't directly affect the given View (such as when a parent component's margin changes).
This means that the given components can be re-positioned (relative to the screen) without them knowing about it.
Because of this I will have to use a different means of notifying my components that they need to remeasure themselves.
I thought about using some of the life-cycle methods that pertain to component updates, but that would most likely be even worse, because most of them get called every time the component updates which is far more often than a layout happens.
Edit: One other problem with using the life-cycle methods that pertain to component updates is if a parent component prevents it's child components from being updated (such as through shouldComponentUpdate).

What is the proper way to integrate tree layout code into React?

I'm building a website using React that shows a tree structure. (In terms of HTML it's not hierarchical at all, but just a bunch of divs at various positions, with SVG to draw connectors.)
To lay out the tree, I use an algorithm (Buchheim et al's improvement on Walker's algorithm) which looks at the widths of all the tree nodes and calculates the appropriate positions for each node.
I'm having trouble integrating this the "right" way. I cannot know the node's width until I'm inside its componentDidMount function, but at that time I don't know the position yet. So I need to change the position later, and modifying the DOM afterwards doesn't seem the React way. Making sure the tree component can access the nodes also feels slightly hackier than it perhaps needs to be.
Here's what I do now:
The tree component's render function generates an array of node components.
I've added some additional dictionaries directly to the tree component to store data I need. I found no way to use props or state for this. (I copied this from the Masonry mixin.)
The node component's componentDidMount function calls a callback on the tree component to register its DOM node there. This feels pretty awful but it works.
The tree component's componentDidMount function lays out the tree, sets the positions of the DOM nodes, and draws the connections between the nodes using SVG in a div that is not managed by React. That also feels a bit dirty but it's more because I needed to quickly get something that worked.
The main thing I dislike is having to sneak in parallel data structures to be able to change the positions of the DOM nodes after the nodes' componentDidMount function has been called.
Is there a cleaner way to do this?

What's the principle inside this js tree?

http://jsbin.com/idala
How is it implemented?
The creator used javascript prototype feature (not the popular Prototype framework) to create a Christmas Tree class named chrisTree.
There are methods implemented to perform the 'drawing' of the tree as well as the animation by:
Dynamically creating DOM elements to represent the trees
Manipulation of CSS styles
Using setTimeout method for animation effect triggering
etc. etc.
Nice work!
Javascript making use of x, y coordinates, arrays and bit of css. You got to analyze it closely to get an idea of how it works. Of course you should have good understanding of javascript. :)
Creating div elements which are absolutely positioned so that it appears as a tree.
Settimeout used to light up divs
Colors are stored in an array
You can view the code using the following link and understand what is being done
http://jsbin.com/idala/edit

javascript/dom -- how expensive is creating vs rearranging dom nodes?

I'm trying to optimize a sortable table I've written. The bottleneck is in the dom manipulation. I'm currently creating new table rows and inserting them every time I sort the table. I'm wondering if I might be able to speed things up by simple rearranging the rows, not recreating the nodes. For this to make a significant difference, dom node rearranging would have to be a lot snappier than node creating. Is this the case?
thanks,
-Morgan
I don't know whether creating or manipulating is faster, but I do know that it'll be faster if you manipulate the entire table when it's not on the page and then place it on all at once. Along those lines, it'll probably be slower to re-arrange the existing rows in place unless the whole table is removed from the DOM first.
This page suggests that it'd be fastest to clone the current table, manipulate it as you wish, then replace the table on the DOM.
I'm drawing this table about twice as quickly now, using innerHTML, building the entire contents as a string, rather than inserting nodes one-by-by.
You may find this page handy for some benchmarks:
http://www.quirksmode.org/dom/innerhtml.html
I was looking for an answer to this and decided to set up a quick benchmark http://jsfiddle.net/wheresrhys/2g6Dn/6/
It uses jQuery, so is not a pure benchmark, and it's probably skewed in other ways too. But the result it gives is that moving DOM nodes is about twice as fast as creating and detroying dom nodes every time
if you can, it is better to do the dom manipulation not as actual dom manipulation, but as some sort of method within your script and then manipulating the dom. So rather than doing what is called a repaint on every single node, you clump what would have been your repaint on every single node into its own method, and then attach those nodes into a parent that would then be attached to the actual dom, resulting in just two repaints instead of hundreds. I say two b/c you need to cleanup what is in the dom already before updating with your new data.

Categories