Detailed explanation of web page performance management

Have you ever encountered a web page with poor performance?

This kind of webpage responds very slowly, takes up a lot of CPU and memory, often freezes when browsing, and the animation effect of the page is not smooth.

How will you react? I suspect that most users will close this page and visit other sites instead. As a developer, you definitely don't want to see this, so how can you improve performance?

This article will detail the causes of performance issues and how to fix them.

The process of creating a web page

To understand why web pages are not performing well, it is necessary to understand how web pages are generated.

The web page generation process can be roughly divided into five steps.

  1. HTML code into DOM
  2. Convert CSS code into CSSOM (CSS Object Model)
  3. Combine DOM and CSSOM to generate a render tree (containing visual information for each node)
  4. Generate layout (layout), that is, flat composite all nodes of all render trees
  5. paint the layout on the screen

Among these five steps, the first to third steps are very fast, and the time-consuming steps are the fourth and fifth steps.

The two steps of "generating layout" (flow) and "painting" are collectively called "rendering".

Second, rearrange and redraw

When a web page is generated, it will be rendered at least once. In the process of user access, it will continue to re-render.

The following three situations will cause the web page to be re-rendered.

  • Modify the DOM
  • Modify the style sheet
  • User events (such as mouseover, page scrolling, typing in an input box, window resizing, etc.)

Re-rendering requires re-layout and re-painting. The former is called "reflow" (reflow), the latter is called "redraw" (repaint).

It should be noted that "redrawing" does not necessarily require "reflowing". For example, changing the color of a web page element will only trigger "redrawing", not "reflowing", because the layout has not changed. However, "reflow" will inevitably lead to "redraw", for example, changing the position of a web page element will trigger both "reflow" and "redraw" because the layout has changed.

3. The impact on performance

Reflows and repaints will keep triggering, it's inevitable. However, they are very resource intensive and are the root cause of poor web page performance.

To improve web page performance is to reduce the frequency and cost of "reflow" and "redraw", and trigger re-rendering as little as possible.

As mentioned earlier, DOM changes and style changes will trigger re-rendering. However, the browser is already very smart and will try to group all the changes together, queue them up, and execute them all at once, trying to avoid multiple re-renders.


div.style.color = 'blue'; div.style.marginTop = '30px'; 

In the above code, the div element has two style changes, but the browser will only trigger one reflow and repaint.

If it is poorly written, it will trigger two reflows and repaints.


div.style.color = 'blue'; var margin = parseInt(div.style.marginTop); div.style.marginTop = (margin + 10) + 'px'; 

After the above code sets the background color of the div element, the second line asks the browser to give the position of the element, so the browser has to reflow immediately.

Generally speaking, after a style write operation, if there is a read operation of the following properties, it will cause the browser to re-render immediately.

  • offsetTop/offsetLeft/offsetWidth/offsetHeight
  • scrollTop/scrollLeft/scrollWidth/scrollHeight
  • clientTop/clientLeft/clientWidth/clientHeight
  • getComputedStyle()

Therefore, from a performance point of view, try not to put read operations and write operations in one statement.


// bad
div.style.left = div.offsetLeft + 10 + "px"; div.style.top = div.offsetTop + 10 + "px";  // good var left = div.offsetLeft; var top = div.offsetTop; div.style.left = left + 10 + "px"; div.style.top = top + 10 + "px"; 

The general rules are:

  • The simpler the stylesheet, the faster it will reflow and repaint.
  • The higher the level of DOM elements to reflow and repaint, the higher the cost.
  • The cost of rearranging and redrawing table elements is higher than that of div elements

4. Nine tips to improve performance

There are some tricks to reduce the frequency and cost of browser re-rendering.

The first one is mentioned in the previous section, multiple read operations (or multiple write operations) of the DOM should be put together. Do not add a write operation between two read operations.

Second, if a style is obtained by reflowing, it is better to cache the result. Avoid reflowing the browser the next time you use it.

Third, don't change the style one by one, but change the style all at once by changing the class or csstext property.


// bad
var left = 10; var top = 10; el.style.left = left + "px"; el.style.top = top + "px";  // good el.className += " theclassname";  // good el.style.cssText += "; left: " + left + "px; top: " + top + "px;"; 

Fourth, try to use offline DOM instead of real web DOM to change element styles. For example, operate the Document Fragment object, and then add this object to the DOM after completion. For another example, use the cloneNode() method to operate on the cloned node, and then replace the original node with the cloned node.

The fifth item, first set the element as display: none(requires 1 rearrangement and redraw), then perform 100 operations on this node, and finally restore the display (requires 1 rearrangement and redraw). That way, you're replacing potentially up to 100 re-renders with two re-renders.

Article 6, for elements whose position attribute is absoluteor fixed, the reordering overhead will be relatively small, because it does not need to consider its impact on other elements.

Seventh, only when necessary, make the display property of the element visible, because invisible elements do not affect reflow and redraw. In addition, visibility : hiddenthe element only affects repainting, not reflowing.

The eighth, use the script library of virtual DOM, such as React, etc.

Item 9, use the two methods window.requestAnimationFrame() and window.requestIdleCallback() to adjust re-rendering (see below for details).

5. Refresh rate

Many times, intensive re-rendering is unavoidable, such as scroll event callbacks and web animations.

Each frame of web animation is a re-render. At animations below 24 frames per second, the human eye can feel the pause. General web animation needs to reach a frequency of 30 to 60 frames per second in order to be relatively smooth. If it can reach 70 or even 80 frames per second, it will be extremely smooth.

Most monitors have a refresh rate of 60Hz, and in order to be consistent with the system and to save power, the browser will automatically refresh animations at this rate (if it can).

Therefore, if the web page animation can achieve 60 frames per second, it will be refreshed synchronously with the display to achieve the best visual effect. This means that 60 re-renders are made in one second, and each re-render cannot take longer than 16.66 milliseconds.

How many re-renders can be completed in one second, this indicator is called "refresh rate", which is FPS (frame per second) in English. 60 re-renders is 60FPS.

If you want to achieve a refresh rate of 60 frames, it means that each task of the JavaScript thread must take less than 16 milliseconds. One solution is to use Web Worker, the main thread is only used for UI rendering, and then tasks unrelated to UI rendering are placed on the Worker thread.

6. Timeline panel of developer tools

The Timeline panel of Chrome's developer tools is the best tool for viewing the "refresh rate". This section describes how to use this tool.

First, press F12 to open the "Developer Tools" and switch to the Timeline panel.

There's a gray dot in the upper left corner, which is the record button, and when you press it, it turns red. Then, do something on the web page and press the button again to finish recording.

The Timeline panel provides two viewing methods: the horizontal bar is "Event Mode", which shows the time spent on re-rendering various events; the vertical bar is "Frame Mode", which shows each frame where is the time spent.

First look at the "event pattern", you can judge from it, which part of the performance problem occurs, is it the execution of JavaScript, or the rendering?

Different colors represent different events.

  • Blue: network communication and HTML parsing
  • Yellow: JavaScript execution
  • Purple: Style calculation and layout, i.e. reflow
  • green: redraw

Which color block is more, it means that the performance is spent there. The longer the color block, the bigger the problem.

Frames mode is used to view the time consumption of a single frame. The lower the color bar height of each frame, the better, which means less time-consuming.

As you can see, the frame mode has two horizontal guides.

The bottom one is 60FPS, below this line, you can reach 60 frames per second; the top one is 30FPS, below this line, you can reach 30 renderings per second. If the color bars are more than 30FPS, the page will have performance problems.

In addition, you can also view the time-consuming situation of a certain interval.

Or click on each frame to see the temporal composition of that frame.

七、window.requestAnimationFrame()

There are some JavaScript methods to adjust re-rendering, which can greatly improve web page performance.

The most important of them is the window.requestAnimationFrame() method. It can put some code to execute on the next re-render.


function doubleHeight(element) { var currentHeight = element.clientHeight; element.style.height = (currentHeight * 2) + 'px'; } elements.forEach(doubleHeight); 

The above code uses a loop operation to double the height of each element. However, every time through the loop, a read operation is followed by a write operation. This triggers a lot of re-renders in a short period of time, which is obviously bad for web page performance.

We can use window.requestAnimationFrame()to separate read and write operations, and put all write operations on the next re-render.


function doubleHeight(element) { var currentHeight = element.clientHeight; window.requestAnimationFrame(function () { element.style.height = (currentHeight * 2) + 'px'; }); } elements.forEach(doubleHeight); 

The listener function of the page scroll event (scroll) is very suitable for using window.requestAnimationFrame() to postpone until the next re-rendering.


$(window).on('scroll', function() { window.requestAnimationFrame(scrollHandler); }); 

Of course, the most applicable occasion is still web animation. Below is an example of a rotation animation where the element rotates 1 degree per frame.


var rAF = window.requestAnimationFrame;

var degrees = 0; function update() { div.style.transform = "rotate(" + degrees + "deg)"; console.log('updated to degrees ' + degrees); degrees = degrees + 1; rAF(update); } rAF(update); 

八、window.requestIdleCallback()

There is also a function window.requestIdleCallback() which can also be used to adjust re-rendering.

It specifies that the callback function will only be executed if there is free time at the end of a frame.


requestIdleCallback(fn);

In the above code, the function fn will be executed only when the running time of the current frame is less than 16.66ms. Otherwise, it is postponed to the next frame, if there is no free time for the next frame, it is postponed to the next frame, and so on.

It can also accept a second parameter that represents the specified number of milliseconds. If there is no idle time for each frame within the specified period of time, then the function fn will be forced to execute.


requestIdleCallback(fn, 5000); 

The above code means that the function fn will be executed after 5000 milliseconds at the latest.

The function fn can accept a deadline object as a parameter.


requestIdleCallback(function someHeavyComputation(deadline) { while(deadline.timeRemaining() > 0) { doWorkIfNeeded(); } if(thereIsMoreWorkToDo) { requestIdleCallback(someHeavyComputation); } }); 

In the above code, the parameter of the callback function someHeavyComputation is a deadline object.

The deadline object has one method and one property: timeRemaining() and didTimeout.

(1) timeRemaining() method

The timeRemaining() method returns the milliseconds remaining in the current frame. This method can only be read, not written, and will be updated dynamically. So it is possible to keep checking this property, and if there is still time left, keep performing certain tasks. Once this property equals 0, the task is assigned to the next round requestIdleCallback.

In the previous example code, the doWorkIfNeeded method is continuously called as long as the current frame has free time. Once there is no free time, but the task has not been fully executed, it is allocated to the next round requestIdleCallback.

(2) didTimeout attribute

The property of the deadline object  didTimeout returns a boolean value indicating whether the specified time has expired. This means that if the callback function fires because the specified time has expired, then you will get two results.

  • timeRemaining method returns 0
  • didTimeout property equals true

Therefore, if the callback function is executed, there are only two reasons: the current frame has idle time, or the specified time is up.


function myNonEssentialWork (deadline) {
  while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0) doWorkIfNeeded(); if (tasks.length > 0) requestIdleCallback(myNonEssentialWork); } requestIdleCallback(myNonEssentialWork, 5000); 

The above code ensures that the doWorkIfNeeded function will be executed repeatedly at some idle time in the future (or after the specified time expires).

requestIdleCallback is a very new function, just introduced to the standard, currently only supported by Chrome, but other browsers can use the shim library .

9. Reference Links

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325070311&siteId=291194637