“This is the sixth day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
Q: Why is DOM so slow
1.1 Because of “Toll”
The JS engine and rendering engine (browser kernel) are implemented independently. When we manipulate the DOM with JS, there is essentially “cross boundary communication” between the JS engine and the rendering engine. The implementation of this “cross-border communication” is not simple and relies on bridging interfaces as “Bridges” (see figure below).
There is a charge to cross the bridge — an expense that is not negligible in itself.
Every time we manipulate the DOM, either to modify it or just to access its value, we have to bridge it.
The more times you cross the bridge, the more obvious performance problems will occur. So the advice to “cut down on DOM manipulation” is not entirely groundless.
1.2 Changes to the DOM cause style changes
Crossing the bridge was slow, and so were the results of our change operations across the bridge.
Most of the time, we don’t just access the DOM, we modify it. Backflow or redraw is triggered when our changes to the DOM cause changes in its appearance (style).
This process is essentially due to our DOM modification triggering a change in the Render Tree:
Graph TD RendreTree --> Redraw --> Redraw
Reflux (rearrangement) :
When we make changes to the DOM that result in a change in the GEOMETRY of the DOM (such as changing the width or height of an element, or hiding an element), the browser recalculates the element’s geometry (which affects the geometry and position of other elements) and then draws the calculated results. This process is called backflow (also known as rearrangement).
Redraw:
When we make changes to the DOM that result in a style change without affecting its geometry (such as changing the color or background color), the browser doesn’t need to recalculate the element’s geometry and simply draw a new style for the element (skipping the backflow shown above). This process is called redrawing.
Redrawing does not necessarily lead to backflow, backflow does lead to redrawing. By comparison, backflow does more work and costs more. But both are ultimately sexual.
2. The cure: Speed up your DOM
2.1 Reduce DOM operations: pay less “tolls” and avoid excessive rendering
The problem code
for(var count=0; count<10000; count++){document.getElementById('container').innerHTML+=' I am a quiz '
}
Copy the code
The optimized
let container = document.getElementById('container')
let content = ' '
for(let count=0; count<10000; count++){// Start with the content
content += ' I am a small test '
}
// When the content is processed, DOM changes are triggered
container.innerHTML = content
Copy the code
In fact, consider the fact that JS runs much faster than DOM. The core idea behind reducing DOM manipulation is to let JS partial pressure the DOM.
We used innerHTML directly to concatenate the target content, which is useful but not elegant.
DOM fragments, by contrast, can help us achieve the same goal in a more structured way, keeping our code extensible and maintainable while maintaining performance.
2.2 the DOM fragments
The DocumentFragment interface represents a minimal document object that has no parent file. It is used as a lightweight version of Document to store formatted or poorly formatted XML fragments. Because the DocumentFragment is not part of the real DOM tree, its changes do not cause reflow of the DOM tree or performance issues.
In our example above, the string variable Content acts as a DOM Fragment. Both string variables and DOM fragments are essentially containers that are separated from the real DOM tree and used to cache DOM operations of batches.
We used innerHTML directly to concatenate the target content, which is useful but not elegant. DOM fragments, by contrast, can help us achieve the same goal in a more structured way, keeping our code extensible and maintainable while maintaining performance. Let’s now rewrite the above example using DOM fragments:
Further optimization
let container = document.getElementById('container')
// Create a DOM Fragment object as a container
let content = document.createDocumentFragment()
for(let count=0; count<10000; count++){// Span can now be created using the DOM API
let oSpan = document.createElement("span")
oSpan.innerHTML = 'I'm a little test.'
// Manipulate the DOM Fragment object as if it were a real DOM
content.appendChild(oSpan)
}
// After the content is processed, the actual DOM changes are triggered
container.appendChild(content)
Copy the code
When we run this code, we get the same results as the previous two. As you can see, the DOM Fragment object allows us to call various DOM apis as if we were manipulating the real DOM, thus ensuring the quality of our code.
And its identity is very pure: when we try to append it into the real DOM, it will surrender all of its cached descendants and get out of the way, perfectly fulfilling its role as a container and not in the real DOM structure.
This structured and clean feature makes DOM Fragment popular as a classical means of performance optimization, which is reflected in the source code of jQuery, Vue and other excellent front-end frameworks.