Avoid Large, Complex Layouts and Layout Thrashing
Layout is where the browser calculates the geometric information of elements: that is, the size and position of elements in the page. Each element will have explicit or implicit size information based on the CSS used, the content of the element, or the parent element. This process is called layout in Chrome, Opera, Safari and Internet Explorer
In Firefox, it is called reflow, but the process is actually the same.
Similar to style calculation, the direct focus of layout cost is:
- The number of elements that need to be laid out.
- The complexity of these layouts.
In short:
- Layouts are usually limited to the entire document.
- The number of DOM elements affects performance; You should avoid triggering the layout as much as possible.
- Evaluate the performance of the layout model; New flexboxes are usually faster than old flexboxes or floating based layout models.
- Avoid forced synchronization layout and layout jitter; Read style values and make style changes.
When you change the style, the browser checks to see if there are any changes that require the layout to be calculated, and if the render tree needs to be updated. Changing geometric properties, such as width, height, left, or top, requires a layout process.
.box { width: 20px; height: 20px; } /** * Changing width and height * triggers layout. */ .box--expanded { width: 200px; height: 350px; }
Layout almost always works on the entire document. If you have many elements, it will take a long time to figure out their location and size.
If layout cannot be avoided, the key is to use Chrome DevTools again to see how long it takes and determine whether the layout is the cause of the bottleneck. First, open DevTools, go to the Timeline tab, click record and interact with your site. When you stop recording, you will see a breakdown of your website performance:
In the above example, when we delve into the frame, we see that it takes more than 20 milliseconds inside the layout. When we have 16 milliseconds to display the frame on the screen in the animation, this is too high. You can also see that DevTools will tell you the size of the tree (1618 elements in this case) and the number of nodes that need to be laid out.
Avoid forced synchronous layoutsShipping web pages to the screen has the following order:
First run JavaScript, then style calculation, and then layout. However, you can use JavaScript to force the browser to execute the layout ahead of time. It is called forced synchronization layout.
The first thing to remember is that when JavaScript runs, all the old layout values in the previous frame are known for you to query. So, for example, if you want to write the height of the element at the beginning of the frame (let's call it "box"), you can write the following code:
// Schedule our function to run at the start of the frame. requestAnimationFrame(logBoxHeight); function logBoxHeight() { // Gets the height of the box in pixels and logs it out. console.log(box.offsetHeight); }
If you change the style of the box before asking for the height, things will become problematic:
function logBoxHeight() { box.classList.add('super-big'); // Gets the height of the box in pixels // and logs it out. console.log(box.offsetHeight); }
Now, in order to answer the height question, the browser must first apply the style change (because of the extra large class) and then run the layout. Only in this way can it return to the correct height. This is unnecessary and can be expensive work.
Therefore, you should always read styles in batches and execute them first (the browser can use the layout value of the previous frame), and then perform any writing:
The correct completion of the above functions will be:
function logBoxHeight() { // Gets the height of the box in pixels // and logs it out. console.log(box.offsetHeight); box.classList.add('super-big'); }
In most cases, you do not need to apply styles and then query values; It is sufficient to use the value of the last frame. Running style calculations and layouts synchronously earlier than the browser expected is a potential bottleneck, not what you usually want to do.
Avoid layout thrashingOne way to make forced synchronous layouts worse is to do a large number of layouts quickly and continuously. Look at this Code:
function resizeAllParagraphsToMatchBlockWidth() { // Puts the browser into a read-write-read-write cycle. for (var i = 0; i < paragraphs.length; i++) { paragraphs[i].style.width = box.offsetWidth + 'px'; } }
This code iterates through a set of paragraphs and sets the width of each paragraph to match the width of the element named "box". It looks harmless, but the problem is that each iteration of the loop reads a style value (box.offsetWidth) and then immediately uses it to update the paragraph width (paragraphs[i].style.width). In the next iteration of the loop, the browser must consider the fact that the style has changed since the last request for offsetWidth (in the previous iteration), so it must apply the style change and run the layout. This will happen in each iteration!.
The fix for this example is to read and write the value again:
// Read. var width = box.offsetWidth; function resizeAllParagraphsToMatchBlockWidth() { for (var i = 0; i < paragraphs.length; i++) { // Now write. paragraphs[i].style.width = width + 'px'; } }
More Jerry's original articles are: "Wang Zixi":