Wang Yu

I joined Qunar in 2016 and now work as a front end engineer in YMFE of Qunar Platform Division.

Visit the team’s blog YMFE (http://ymfe.tech) for more technical sharing.

Recently, I was working on a project that had some performance issues at run time. I read a lot about performance optimization.

Front-end performance optimization, including CSS/JS performance optimization, network performance optimization and so on, this aspect of the content “high performance website construction guide”, “high performance website construction advanced guide”, “high performance JavaScript” and so on books have done a lot of explanation, strongly recommended reading. (See the list at the end of this article.)

Most of the content below is covered in the books mentioned above, so consider reading them instead, getting a complete understanding, and not reading any more for this article.

If you’re following through, let’s talk about some of the front-end performance issues I’ve encountered and talk about solutions.



1 Prioritize the parts that have a large impact on performance

When an application has a performance problem, don’t jump into the code, think about which parts have the biggest impact on performance first. Prioritizing those parts that have a significant impact on performance can have an immediate effect.

Using Chrome DevTools, you can quickly find out the most important factors that cause performance deterioration. For details on how to use Chrome DevTools, please read the tutorial for Google Developers.

In addition, when optimizing the code, it is also important to pay attention to those areas where there are loops or high frequency calls. Sometimes we may not know if a particular place is executing frequently, such as a callback for certain events. You can use console.count to count the number of executions. When this part of the code that executes frequently is optimized enough, consider whether you can reduce the number of executions. For example, an algorithm of O(n*n*n), no amount of optimization is as fast as turning it into O(n*n).


2 Throttling or quivering for high-frequency triggered events

Never underestimate the frequency with which events like Scroll and Touchmove are executed, and consider whether to add a throttling or quash callback to them. Throttling and throttling, which others may not translate as such, are the functions debounce and throttle.

Debounce and Throttle are similar (but not identical) techniques for controlling how often a function is executed within an event. You can find both functions in underscore or Lodash.

2.1 the use ofdebounceTo eliminate shake

Multiple calls in a row that will actually only be called once. Imagine yourself in an elevator, the door is about to close, at this moment another person comes and cancels the operation of closing, after a while the door is about to close again, another person comes and cancels the operation of closing again. The elevator delays closing until a certain period of time when no one comes.

So Debounce is suitable for scenarios such as validating user input, where multiple triggers only need to respond to the last one.

2.2 the use ofthrottlethrottling

Restrict frequently called functions to a given call frequency. It guarantees that no matter how often a function is called, it can only be called once within a given event. For example, if you want to check the current scroll position while scrolling to show or hide the back to the top button, you can throttle the scroll callback to every 300ms.

It should be noted that these two functions are often misused, often without the parties realizing that they are being misused. I’ve used it wrong, and I’ve seen people use it wrong. Both functions take a function as an argument and return a throttled/buffered function. The second usage is the correct one:

$(window). On ('scroll', function() {_. Throttle (doSomething, 300); }); $(window). On ('scroll', _.throttle(doSomething, 200));Copy the code


JavaScript is fast, DOM is slow

JavaScript is fast these days, but the DOM is really slow. So avoid writing things that are hard to read but are said to speed things up. Not long ago, a friend told me that using the ‘+’ sign to convert a string to a number is faster than using parseInt. I had no doubt about this, because it was intuitive that parseInt was making function calls that were probably slower, and we did some validation together on Node V6.3.0, and the results did exactly what we expected, but how much was the difference, 500 million iterations and just 2 seconds faster using the + method. It’s two seconds faster, but actually converting a character to a number might only happen a few times, so it doesn’t make sense, and it just makes the code harder to read.

Plus: ms parseInt 1694.392:3661.403 msCopy the code

What’s really slow is the DOM. The DOM provides apis that JavaScript can call, and the two of them are connected by a bridge, and each time you cross the bridge, you’re charged a lot of money, so try to minimize the number of times you cross the bridge.

3.1 Why is DOM Slow

Here’s a quick explanation of how a browser uses HTML/CSS/JavaScript to render a great page. When the browser receives the HTML Document, it parses the Document and starts building a DOM (Document Object Model) tree, finds the stylesheet in the Document, parses the CSS to build the CSSOM (CSS Object Model) tree, and when both are built, Start building the render tree. The whole process is as follows:

Render the tree building process

After each modification of the DOM or its style, the DOM tree is built, the CSSOM is recalculated, and the new render tree is created. The browser takes advantage of the new render tree to rearrange and redraw pages, as well as merge layers. Typically, browsers rearrange and redraw in batches to improve performance. But when we try to get the size of a node in JavaScript, the browser immediately rearranges it in order to get the real information.

3.2 Avoiding mandatory Synchronous layout

The layout information read in JavaScript is the information from the previous frame. If you modify the layout of the page in JavaScript, for example, adding a class to an element, then read the layout information. In order to get real layout information, the browser needs to force the page layout. This should therefore be avoided.

3.3 Batch DOM Operations

When frequent DOM operations are necessary, you can use a tool like Fastdom, which queues the reading and rewriting of a page and executes it in batches as the page is redrawn, reading first and rewriting later. Because interweaving reads and overwrites can cause multiple page rearrangements. Fastdom prevents this from happening.

Even with tools like Fastdom, there are times when you can’t get to the root of the problem. For example, I recently encountered a situation where a simple interaction with a page (scrolling the page lightly) resulted in thousands of DOM operations, and the key was to reduce the number of DOM operations. At this point, consider the code level to see if there are unnecessary reads.

For additional tips on how to manipulate the DOM efficiently, see the section on High-performance JavaScript, Or check out my book notes, High Performance JavaScript (https://github.com/wy-ei/notebook/issues/34)



4. Optimize rendering performance

The browser typically updates the page 60 times per second at 16.6ms per frame. In order for the browser to maintain a 60fps frame rate, and for the animation to look smooth, it needs to be 60fps, so the logic for each frame needs to be done in 16.6ms.

Each frame actually contains the following steps:

As a result, typically JavaScript execution times should not exceed 10ms.

  • JavaScript: Change element styles, add elements to the DOM, etc

  • Style: The element’s class or Style changes, and the element’s Style needs to be recalculated

  • Layout: The specific dimensions of the elements need to be recalculated

  • Paint: Draw elements on the layer

  • Composite: Combines multiple layers

Of course, this does not mean that every frame does this. When your JavaScript changes a layout property, such as the width and height of an element or top, the browser recalculates the layout and rearranges the entire page.

If you change properties such as background and color that only require redrawing of the page, the layout of the page will not be affected. The browser skips the calculation of layout and just repaints the page.

If you change a property that doesn’t need to evaluate the layout or redraw, then you just merge layers, which is the least expensive change. From csstriggers.com/ you can see that modifying the style properties triggers the actions in Layout, Paint, Composite.

4.1 Place gradients or animate elements in a separate layer

Painting is not done on a single canvas, but on multiple layers. So by elevating the elements that will change to a separate layer, his changes will affect fewer elements.

You can use the CSS will-change: transform; Or the transform: translateZ (0); This will elevate the element to a separate layer.

Use Chrome DevTools to review layers

You can view drawing layers in the Timeline panel of Chrome DevTools while debugging. Of course, it’s not always the case that more layers are better, as adding a new layer can use extra memory. The purpose of adding a new layer is to prevent changes to one element from affecting other elements.

4.2 Reducing the Complexity of drawing

Some properties are more complex to redraw, such as filters and box-shadow or gradients. So don’t abuse these effects.


Optimize JavaScript execution

JavaScript optimizations mentioned below are not about making JavaScript execute faster, but about making JavaScript work with the DOM more efficiently.

5.1 the use ofrequestAnimationFrameTo update the page

We want to make changes to the page at the beginning of each frame, which is currently only possible with requestAnimationFrame. Use setTimeout or setInterval to trigger a function that updates the page. This function may be called in the middle of a frame or at the end of a frame, causing the following frame to be lost.

Using setTimeout may result in frame loss

RequestAnimationFrame schedules the task before the page is redrawn, which ensures that the animation has enough time to execute JavaScript.

5.2 Use Web Workers to process complex calculations

JavaScript is single-threaded and likely to remain so, so JavaScript is likely to block the thread while performing complex calculations, causing the page to feign death. However, the emergence of Web Worker gives us the ability of multi-threading in another way, which can carry out complex calculation in Worker, and send back the result in the form of postMessage when the calculation is completed.

For a single function, because the Web Worker takes a script URL as an argument, using the url.createObjecturl method, we can convert the contents of a function to a URL and use it to create a Worker.

var workerContent = ` self.onmessage = function(evt){ // ... Var result = complexFunc(); // Return the result to self.postmessage (result); }; Var blob = new blob ([workerContent]); var url = window.URL.createObjectURL(blob); Var worker = new worker (url);Copy the code

5.3 Use Transform and opacity to complete animation

Today, only changes to these two properties do not require layout and paint.


6 optimizing CSS

CSS selectors are matched from right to left, so the last selector is often called a key selector because the more special the last selection, the fewer matches need to be made. Never use * (universal selector) as a key selector. Because it matches all elements, the penultimate selector matches all elements again. This leads to very low efficiency.

/* do not do this */ div p * {}Copy the code

In addition, pseudo-class selectors such as first-child are not special enough to be avoided as key selectors. The more special the key selector is, the fewer matches the browser can make to find the element to be matched, and the better the performance of the selector.

Another well-worn caveat is not to use too many selectors. For those of you who are tragically compatible with lower versions of Internet Explorer, avoid USING CSS expressions, which have poor performance. For details, see a note I wrote earlier, The Guide to Building A High Performance Website (https://github.com/wy-ei/notebook/issues/15).



7. Handle scripts and style sheets properly

With requirejs, WebPack, etc., it’s probably rare to load a lot of JavaScript/CSS code into a page. Still, it’s worth talking about how scripts and stylesheets are handled properly.

Most people already know to put JavaScript at the bottom of documents and CSS at the top. Why is that? JavaScript blocks parsing of the page, while external stylesheets block rendering of the page and JavaScript execution.

7.1 CSS blocks rendering

CSS is usually considered a resource that blocks rendering, the page is not rendered until the CSSOM is built, and is placed at the top so that the stylesheet can start loading as soon as possible. But if you put the link that introduced the stylesheet at the bottom of the document, the page will render immediately, but the page will load unstyled and cluttered. When the style sheet is loaded later, the page is redrawn immediately, which is commonly referred to as flickering.

7.2 JavaScript blocks document parsing

When aScript tag is encountered in an HTML document, control is given to JavaScript, and the HTML is not parsed until the JavaScript has downloaded and executed. Therefore, if JavaScript is placed at the top of the document, the JavaScript script will load very slowly, and the user will wait for a long time before the HTML document is parsed into the body part, and the page will be blank.

Another fact that is often overlooked is that: JavaScript will not execute until the browser has downloaded and parsed the CSS file imported with Link, because the style might need to be read in JavaScript before the stylesheet has been loaded back, so the browser will not execute JavaScript. JavaScript can be labeled async to indicate that JavaScript execution does not read the DOM, JavaScript can not be blocked by CSS, and can be executed immediately at idle time.

Above all, you need to make sure your CSS files load quickly.

About this part of the content, “high performance website construction guide” has a very wonderful explanation, wall crack recommended. The Guide to Building High Performance Web Sites, notes I took while reading, can be found here.

Finally strongly recommended reading on performance optimization in the Google Developers series (https://developers.google.com/web/fundamentals/performance).



8 Reference Materials

  1. High Performance Website Construction Guide

    Watercress (8.7 points, https://book.douban.com/subject/3132277/)

  2. Advanced Guide to Building High Performance Web Sites

    Watercress (8.9 points, https://book.douban.com/subject/4719162/)

  3. High Performance JavaScript

    Watercress (8.9 points, https://book.douban.com/subject/5362856/)

  4. Google Developers

    (https://developers.google.com/web/)

  5. Efficient JavaScript

    (https://dev.opera.com/articles/efficient-javascript/?page=3#reflow)

  6. Best Practices for Speeding Up Your Web Site

    (https://developer.yahoo.com/performance/rules.html)

Welcome to leave a message or contribute to share knowledge with us.

Qunar Technology Salon