Translated from: http://kb.cnblogs.com/page/534571/
Always heard Dom is very slow, to try to operate the DOM as little as possible, so I want to go further to explore why everyone will say so, in the online learning some information, this side of the collation.
First, the DOM object itself is also a JS object, so strictly speaking, not the operation of this object is slow, but that after manipulating the object, it will trigger some browser behavior, such as layout and drawing (paint). The following main introduction of these browser behavior, explain how a page is ultimately presented, in addition, from the point of view of the code to illustrate some bad practices and some optimization programs.
How the browser renders a page
A browser has many modules, which are responsible for rendering the page is the rendering engine module, more familiar with WebKit and gecko, etc., here will only involve the content of this module.
Use the text to outline the following process:
- Parse the HTML and generate a DOM tree
- Parse various styles and combine DOM tree to create a render tree
- Calculates layout information for each node of the render tree, such as box position and size
- Draw based on the render tree and take advantage of the browser's UI layer
Where the nodes on the DOM tree and the render tree are not one by one corresponding, such as a " display:none"
node will only exist on the DOM tree, and will not appear on the render tree, because this node does not need to be drawn.
Is the basic process of webkit, in terms and gecko may be different, here paste Gecko flowchart, but the contents of the article will be unified use WebKit terminology.
There are many factors that affect page rendering, such as the location of link affecting the first screen rendering and so on. But here the main focus is on layout-related content.
Paint is a time-consuming process, but layout is a much more time-consuming process, and we cannot be sure that layout must be top-down or bottom-up, and that even a layout involves recalculation of the entire document layout.
But layout is definitely unavoidable, so we mostly want to minimize the number of layout.
What happens when the browser is layout
Before considering how to minimize the number of layout times, you need to know when the browser will be layout.
Layout (reflow) is generally referred to as layouts, which are used to calculate the position and size of elements in a document, which is an important step before rendering. In the first time HTML is loaded, there will be a layout, JS script execution and style changes will also cause the browser to perform layout, this is the main content of this article to discuss.
In general, the layout of the browser is lazy, that is to say: In the JS script execution, is not to update the DOM, any modifications to the DOM will be persisted in a queue, the current JS execution context after the completion of execution, will be based on the changes in the queue to do a layout.
However, sometimes you want to get the latest DOM node information immediately in the JS code, and the browser has to execute layout ahead of time, which is the main cause of DOM performance problems.
The following actions will break the routine and trigger the browser to perform layout:
- Get DOM properties to calculate with JS
- Adding or removing DOM elements
- Resize browser window size
- Change font
- CSS pseudo-class activation, such as: hover
- Modify the DOM element style by JS and the style involves a change in size
Let's go through an example of intuitive feeling under:
Readvar H1 = element1.clientheight;//Write (invalidates layout) Element1.style.height = (H1 * 2) + ' px ';//Read (Trigge RS layout) var H2 = element2.clientheight;//Write (invalidates layout) Element2.style.height = (H2 * 2) + ' px ';//Read (Tri Ggers layout) var h3 = element3.clientheight;//Write (invalidates layout) Element3.style.height = (H3 * 2) + ' px ';
ClientHeight, this property needs to be computed, so it triggers a layout of the browser. Let's take a look at the Chrome (v47.0) developer tool (the timeline record is filtered to show layout only):
In the above example, the code first modifies the style of an element, and then reads the properties of another element clientHeight
, because the previous modification causes the current DOM to be marked as dirty, in order to ensure that this property is accurately obtained, The browser will do a layout once (we find the developer tool of Chrome's conscience hints at our performance issue).
It is easy to optimize this code by pre-reading the required properties and modifying them together.
Readvar h1 = element1.clientheight; var h2 = Element2.clientheight; var h3 = element3.clientheight;//Write (invalidates layout) Element1.style.height = (H1 * 2) + ' px '; Element2.style.height = (H2 * 2) + ' px ';
Take a look at this situation:
Here are some other optimization scenarios.
Solutions to minimize layout
One of the bulk reads and writes mentioned above is a result of getting a property value that needs to be computed, so what are the values that need to be calculated?
In this link, you will be presented with most of the attributes you need to calculate: http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html
Take a look at other things:
Face a series of DOM operations
For a series of DOM operations (DOM element additions and deletions), you can have the following scenarios:
- DocumentFragment
- Display:none
- CloneNode
For example (in the case of DocumentFragment only):
var fragment = Document.createdocumentfragment (); for (var i=0; i < items.length; i++) { var item = document.createelement ("li"); Item.appendchild (document.createTextNode ("Option" + i); Fragment.appendchild (item);} List.appendchild (fragment);
The core idea of this type of optimization is the same: a series of operations on a node that is not on the render tree, and then adding the node back to the render tree, so that no matter how complex the DOM operation, it will only trigger a layout.
Face changes in style
To change the style, we first need to know that not all changes to the style will trigger layout, because we know that layout is about calculating the size and size of the renderobject, so if I just change a color, it will not trigger layout.
Here is a Web site CSS triggers, detailing the effects of each CSS property on the browser's execution of layout and paint.
As in the following case, the optimization is the same as above, the attention of reading and writing.
Elem.style.height = "100px"; Mark invalidated elem.style.width = "100px";
But to mention the animation, this is about JS animation, such as:
function animate (from, to) { if (from = = =) return requestanimationframe (function () {from + = 5 elemen T1.style.height = from + ' px ' animate (from, to)
Each frame of the animation will cause layout, which is unavoidable, but in order to reduce the performance loss of the layout of the animation, you can absolutely position the animation elements, so that the animation elements out of the text flow, layout calculation will be much less.
Using Requestanimationframe
Any operations that could cause repainting should be put intorequestAnimationFrame
In real-world projects, code is divided into modules, which makes it difficult to organize bulk reads and writes as in the previous example. Then you can put the write operation in requestAnimationFrame
the callback, unify the write operation before the next paint to execute.
Readvar H1 = element1.clientheight;//writerequestanimationframe (function () { element1.style.height = (H1 * 2) + ' P x ';}); /Readvar H2 = element2.clientheight;//writerequestanimationframe (function () { element2.style.height = (H2 * 2) + ' px ‘;});
The timing of the animation frame trigger is clearly observed, and the MDN is triggered before the paint, but I estimate that it is executed before the JS script gives control to the browser for the invalidated check of the DOM.
Other points of attention
In addition to the performance issues caused by triggering layout, here are some additional details:
The result of the cache selector, which reduces the DOM query. Special mention should be made here of htmlcollection. Htmlcollection is the document.getElementByTagName
object type that is obtained, and the array type is similar but each time a property of this object is obtained, it is equivalent to a DOM query:
var divs = document.getelementsbytagname ("div"); for (var i = 0; i < divs.length; i++) { //infinite loop document.body.appendChild (document.createelement ("div "));}
For example, the above code will cause an infinite loop, so do some caching when handling htmlcollection objects.
In addition, reducing the nesting depth of DOM elements and optimizing the CSS, removing useless styles will help reduce the amount of layout calculation.
In DOM queries, querySelector
and should be the querySelectorAll
last choice, they are the most powerful, but the execution is very inefficient, if possible, try to substitute other methods.
Below are two links to jsperf that can compare performance.
1) https://jsperf.com/getelementsbyclassname-vs-queryselectorall/162
2) http://jsperf.com/getelementbyid-vs-queryselector/218
My thoughts on the view layer
Above the content of the theoretical aspects of things, from a practical point of view, the above discussion is exactly what the view layer needs to deal with. There is already a library fastdom to do this, but its code is this:
Fastdom.read (function () { console.log (' read ');}); Fastdom.write (function () { console.log (' write ');});
The problem is obvious, leads callback hell
to, and predictably, imperative code like Fastdom lacks extensibility, and the key is that requestAnimationFrame
it becomes an asynchronous programming problem. To let read and write state synchronization, it is necessary to write a wrapper on the basis of the DOM to internal control asynchronous read and write, but all to this, the feeling can be considered directly on the react ...
In short, try to avoid the above mentioned problems, but if you use a library, such as jquery, the layout of the problem is in the library itself abstraction. Like react introduced its own component model, using the virtual DOM to reduce DOM operations, and each time the state changes only once layout, I do not know if there is any use requestAnimationFrame
of the inside, I feel to do a view layer is very difficult, Then prepare to learn react code. I hope that I will come back in a year or two to see this problem, there are some new ideas.
Reference
- http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/
- Https://dev.opera.com/articles/efficient-javascript/?page=3
- http://wilsonpage.co.uk/preventing-layout-thrashing/
- https://www.nczonline.net/blog/2009/02/03/speed-up-your-javascript-part-4/
- Http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html
Why DOM is slow to operate.