Welcome to pay attention to the public number [code attack], more exciting content please pay attention to the latest news of the public number.

preface

In our last article, “Networking for Front-end Performance Optimization,” we analyzed where performance bottlenecks are and how to deal with them from a network perspective. In this chapter, we will cover run-time optimization from the time the request data is received to the time it is used for rendering.

Background: The browser’s rendering process

Runtime performance tuning requires knowing what the browser does once it receives the HTML, so it’s worth generalizing the browser rendering process here.

1. Basic Concepts:

CSS parsing: The process of parsing A CSS style into a CSS Tree. DOM parsing: the process of generating a DOM Tree from all HTML tags on a page; Dom rendering: When the DOM Tree and CSS Tree are combined to produce a Render Tree, rendering a page with styles.

2. Webkit rendering process:

Conclusion and consensus: 1. Js downloading and running will block DOM parsing and rendering. Download and parse of CSS does not block THE parsing of DOM. As can be seen from the figure, DOM parsing does not depend on CSS parsing (it can be regarded as parallel). 3.

When I say JS, I mean normal non-defer and Async types.

HTML optimization

1. Enable gzip compression for web pages

Enable gzip to compress web pages, about 70% of the size can be reduced, the effect is quite impressive.

2. Reduce nesting of HTML tags

The nesting of HTML tags not only increases the burden of DOM parsing and rendering, but also the performance cost of dom lookup and CSS selector lookup.

For example, you often see meaningless nesting (see form and UL elements)

<div class="list">
    <ul>
        <li></li>
    </ul>
</div>
Copy the code

Can be optimized as:

<ul class="list">
    <li></li>
</ul>
Copy the code

3. Reduce meaningless labels

Reducing the number and hierarchy of DOM can reduce the stress of DOM parsing and rendering.

Common ones are: empty tags to float cleanly (though these are currently implemented with pseudo-elements), and decorative tags (images or decorative text)

< div class = "parent" > < p > a lot of content XXXX < / p > < div class = "ft_bg" > < img SRC = "XXXXX" / > < / div > < / div >Copy the code

Can be optimized as

<div class="parent"> <p> <div > <style>. Parent :after{background: url(XXXXX) no-repeat; } </style>Copy the code

4. Delete unnecessary Spaces and newlines in HTML

It is recommended to compress whitespace only for HTML embedded CSS code (style tag) and embedded JS code (Script tag). White space between HTML tags is rendered as a space, which can affect layout and style. Today’s websites use JS rendering, HTML output content is relatively small, so most sites do not do this.

We generally configure in the development process will not delete whitespace, and when the line package to delete whitespace, inconsistency often brings bugs to the page, BAT company’s website generally does not do HTML whitespace compression should also be based on this reason.

As shown in the picture below, taobao home page has compressed the inline CSS and JS, while the HTML is still formatted.

5. Modifier UI uses background property instead of img tag

  • You can use the packaging tool to do CSS Sprite processing CSS images to reduce HTTP requests.
  • Taking advantage of the fact that loading and rendering CSS files do not block DOM parsing, it is possible to speed up DOM rendering by converting serial tasks into parallel tasks.

6. Reduce the loading of JS files for DOM parsing and rendering blocking

Since loading js files blocks DOM parsing and rendering, try to delay the introduction of JS if you don’t have to.

7. Avoid blank SRC for images

When the SRC of the image is empty, the browser assumes that this is the default value and loads the current page a second time.

8. Avoid resizing images

Image size changes can cause backflow and redraw, affecting performance.

CSS optimization

1. The CSS of the first screen is inlined

Everyone is used to CSS content through the link tag, which may be ready to render after dom parsing, CSS content has not been downloaded, delaying the DOM rendering time, thus affecting the user to display. One drawback to this is that it doesn't take advantage of HTTP caching and reloads each time, but keeping the first screen size is not a problem. At present, the BAT website will do this optimization.Copy the code

2. Use inheritance to reduce code

In CSS, many properties can be inherited, such as line-height, color, font, etc., reducing redundant code writing. ` ` ` < div class = "cont" > < p >... </p> </div> ``` ``` .cont{color: #999} .cont p{color: #999} ```Copy the code

3. Use CSS compound properties to reduce code

Try to use CSS compound attributes (border, font, background, margin, etc.), reduce the amount of code, such as' 'p{background-image: url(XXXXX); background-repeat: no-repeat; background-postion: center center; } // Optimize to p{background: urr(XXX) no-repeat center center; Properties can be constructed as compound properties in the packaging phase using tools.Copy the code

4. CSS selector optimization

CSS selector matching is performed from right to left, so different writing styles can affect the performance of CSS selector matching. The following points need to be noted: 1. The selector should not be nested too long such as.content.list. item a{} performance will be much lower than '.content_item'. 2. Avoid the use of wildcards and attribute selectors. Because wildcards and attribute selectors match too many elements, the browser will have to configure them from too many elements, so avoid using the 3. > Modern browsers do a lot of optimization for selector matching, so there is not much difference in performance between different selectors.Copy the code

5. Avoid unnecessary backflow and repainting

Proper use of some CSS properties can avoid unnecessary drawing or backflow. For example, in a CSS animation, if you want to move an element, draw it with top/left and position: Absolute, which triggers backflow. If the transform is used to control the displacement of the element, no backflow will be triggered because the composition thread's GPU is used to draw.Copy the code

6. Reduce unnecessary rendering

Using display: None will not render the element, but using visibility:hidden will still render the element, which has the same effect as opacity:0.Copy the code

7. Use Flex in preference to float and position

Flex performs better than float and Position, so use it whenever possible.

JS optimization

1. Reduce reflow and Repaint

Reflow, commonly called reflux or rearrangement, is a recalculation of page layout.

Backflow is a performance drain because changes to an element result in the retypesetting and drawing of all elements following that element.

By definition, any behavior that causes a change in the position an element occupies, as well as a change in the absolute position of the elements around it, causes reflow.

What are the behaviors that cause reflux?

<1>. Frequent dom manipulation

For example, if you want to delete/modify some nodes or add child elements to a parent element, such operations will cause backflow.

What to do: You can use a DocumentFragment and then manipulate the DocumentFragment into the DOM when you are ready.

var elFragment = document.createDocumentFragment();

for(var i = 0 ; i < 10; i ++) {
    var p = document.createElement("p");
    var text = document.createTextNode(`段落${i}`);
    p.appendChild(text);
    elFragment.appendChild(p);
}

document.body.appendChild(elFragment);
Copy the code
<2>. Changes in geometry properties (position, size)

For example, the width and height of elements change; For example, changes in margin, padding, border, etc. of an element cause the page layout to change.

Solution: When you need to modify multiple attributes, you should place them in a single class and operate only on that class to reduce backflow. Or use cssText to manipulate multiple CSS properties at once.

document.body.style.color = '#fff'; document.body.style.border = 'solid 1px red'; / / optimization for the document body. Style. CssText = 'color: # FFF; border: solid 1px red; 'Copy the code
<3>. Gets the offset attribute of the element

For example, to get an element’s scrollTop, scrollWidth, offsetTop, offsetWidth attributes, the browser will backflow to get the latest value to ensure that the value is correct.

Solution: Cache the obtained values and use the cached data the next time.

Repaint.

Because pure Repaint only repaints itself, repaint is likely to be much less costly than Reflow in terms of performance by avoiding repeated, frequent changes to CSS properties.

2. Anti-shake and throttle

When processing events such as resize, scroll and mousemove, many times will be triggered in a short period of time, but we do not want them to be executed many times. Therefore, certain judgment logic is required to ensure that they are executed only once or at a certain frequency in a short period of time or under a certain threshold.

Function throttling and stabilization are methods to save browser CPU resources and optimize page smoothness by optimizing high frequency execution of functions.

Throttling: Refers to the rhythmic execution of a function, rather than the execution of a function once an event is triggered.

Common scenarios: lazy loading; Mouse move goods magnifying glass effect;

document.onscroll = function(){
	console.log('hello')
}
Copy the code

The above code mouse wheel scroll, will output dozens of hundreds of “Hello”.

I want the function to be executed every 100ms while the mouse is scrolling to suffice. The optimization is as follows:

var canIRun = true; document.onscroll = function(){ if(canIRun){ canIRun = false; setTimeout(function(){ console.log('hello') canIRun = true; }}}, 100)Copy the code

We can use the anti-jitter function of loadsh

_.throttle(function(){
	console.log('hello')
}, 100)
Copy the code

Stabilization: The function does not execute while the event continues to fire, and does not execute until a period of time has passed after the event has not been fired.

Common scenarios: Real-time verification of input in email or mobile phone format; When the mouse moves over the page, it pauses to do something. When searching, type suggest, etc.

document.onscroll = function(){
	console.log('hello')
}
Copy the code

The above code mouse wheel scroll, will output dozens of hundreds of “Hello”.

I want the mouse to stop scrolling, after a period of time stable output. The optimization is as follows:

var timer = null;
document.onscroll = function(){
    clearTimeout(timer);
    timer = setTimeout(function(){
        console.log('hello')
    }, 100)
}
Copy the code

We can use the anti-jitter function of loadsh

_.debounce(function(){
	console.log('hello')
}, 100)
Copy the code

3. Lazy loading of images

Lazy loading is lazy loading, loading when needed. For non-first screen images, a default image address is used instead, and the real image address is loaded after entering the viewable area.

Principle: Monitor scroll event to judge the length of page scrolling and the position of lazily loaded content in the page. When they cross or are about to cross, write the real URL of the picture into the SRC attribute, so as to achieve the purpose of lazy loading.

4. Load on demand [Package content and unpack]

By default, a single page application exports the JS and CSS files respectively. If the project is too large, the static file will be too large. You can divide the code according to the page, pack it into multiple JS and CSS files, and load and use them according to the page route.

    1. Segmentation is done through a multi-entry configuration
// webpack.config.js

module.exports = {
	entry:['./index.js','./second.js'],
	// ...
}
Copy the code

Disadvantages: This method is the simplest, but causes the duplicate reference module to be repeatedly punched into the package.

    1. Split code through dynamic code loading (use import() in code).

React.lazy(() => import(‘./Demo’))

    1. Use Optimizition. Splitchunks for code segmentation.
// webpack.config.js

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 20000,
      minRemainingSize: 0,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

Copy the code

5. Clear the timer

Because the timer is triggered by a delay, it is necessary to clear the timer in time when it is no longer needed; otherwise, useless tasks will still occupy resources.

Especially at present many applications are single-page applications, not timely cleaning will make the page useless tasks will be more and more.

When a component is destroyed, run the clearTimeout and clearInterval commands to clear timers.

// vue
beforeDestroy(){
	clearInterval(this.timer);
    clearTimeout(this.timer);
}

// react
componentWillUnmount(){
	clearInterval(this.timer);
    clearTimeout(this.timer);
}
Copy the code

6. Use requestAnimationFrame instead of setInterval/setTimeout

  1. Multiple RequestAnimationFrames can work at the same time, whereas setTimeout needs to be drawn independently;
  2. When a page loses focus, the browser stops drawing the page, requestAnimationFrame stops drawing and synchronizes with the browser, saving resources.
  3. RequestAnimationFrame execution is synchronized with the browser frame rate, regardless of the execution event interval.

Refer to the MDN

const element = document.getElementById('some-element-you-want-to-animate'); let start; let count = 0; function step(timestamp) { if (start === undefined) { start = timestamp; } const elapsed = timestamp - start; // Use 'math.min ()' here to make sure the element stops at exactly 200px. Element.style. transform = 'translateX(' + Elapsed, 200) + 'px '; If (Elapsed < 1000) {// Stop animation console.log(count) after 1 SEC; / / statistics under 1 second browser rendering about how much the frame window. RequestAnimationFrame (step); } } window.requestAnimationFrame(step);Copy the code

The above code, the use of the window. RequestAnimationFrame (step), step functions are executed according to the frame rate of the browser, and of course use window. The setTimeout (30) step, also can, but the effect will be worse.

7. Take advantage of the idle period of the JS thread

Window. RequestIdleCallback () method will be called function in browser free time line. With the help of the requestIdleCallback API, you can make full use of idle JS threads to handle low-priority tasks. Refer to the MDN

In the React 16.x version, the new scheduler, Fiber, is optimized to take advantage of this API.

8. Web Worker

Since the Worker thread is independent from the main thread and can run at the same time with the main thread, some recalculative tasks unrelated to DOM operation can be handed over to the Worker thread for calculation, which can improve the efficiency of calculation without blocking the rendering of the main thread. MDN reference

For example, if there is a task that needs to be cycled 1,000 times, the worker can be given to do it, and then the main thread will be told to display it after completion. (Communicate via postMessage and addEventListener)

// main.js

var worker = new Worker('http://localhost:8080/work.js');
worker.postMessage('hello');

worker.onmessage = function (event) {
  window.alert('Received message ' + event.data);
}

Copy the code
// worker.js
this.addEventListener('message', function (e) {
  if(e.data == 'hello'){
  	doLoop();
  }
}, false);

function doLoop(){
	let str = '';
	let count = 1000;
	while(count > 0){
    	str += 1;
        count++;
    }

    this.postMessage(str);
}
Copy the code

9. Request preloading

Unlike preload and Prefetch mentioned in the previous article, the data is requested in advance at the right time, and then the cache can be used to calculate when it is actually used. For example: PC page click a button popup to display a list, then you can hover button when the mouse request interface data; The page scrolls to a certain location to initiate the action of requesting the interface.

10. Client Rendering (CSR)

Now many pages are running on the Webview of our app. We can use the client to render the first screen content and optimize the serial tasks of WebView startup and page loading into parallel tasks of WebView startup and page loading.

Client support is required.

11. Framework class code is introduced by CDN alone

During application construction, react, Vue, AXIos and other contents that do not need to be modified are excluded from the packaged content through the externals attribute of Webpack and introduced into HTML through independent CDN. This makes efficient use of the HTTP cache of CDN resources.

// index.html <script crossorigin src="//unpkg.com/react@17/umd/react.production.min.js"></script> <script crossorigin src="//unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> <script SRC = "/ / cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js" > < / script >Copy the code
// webpack.config.prod.js

module.exports = {
	...
	externals: {
    	'react': 'React',
        'react-dom': 'ReactDOM',
        'axios': 'Axios'
    }
}
Copy the code

12.WebAssembly

Official description: as a portable, small, fast loading and Web compatible new format. Those of you who are interested can do some exploration.

13. Optimization of long lists — virtual lists

Long list rendering is a disaster for MVVM frameworks nowadays, especially when it is common for mobile stream products to render hundreds or thousands of entries. The community has a more mature solution — virtual lists. The essence of virtual list optimization is to render fewer nodes and only the visible elements of the currently visible window. Here’s a picture on the web to illustrate.

Application scenarios: Infinite scrolling list, chat window, timeline, etc.

14. Framework usage optimization — React

A. The react

1. Optimize shallow comparison updates with pureComponent
import {PureComponent} from 'react'

class Demo extends PureComponent{

}
Copy the code

The PureComponent implements shouldComponentUpate() by using a shallow contrast between props and state.

Optimize react.componentPonent with shouldComponentUpate
import {Component} from 'react'

class Demo extends Component{
	shouldComponentUpate(nextProps, nextState){
    	// do something
    }
}
Copy the code

The difference between a PureComponent and a Component is that the PureComponent compares props to state, and updates the Component if it is inconsistent.

3. Use key

React determines whether to destroy and recreate the component or update it based on whether the key has changed.

	arr.map(item => {
		return <div key={item.title}>
	        <p>{item.title}</p>
        </div>
	})
Copy the code

The value of Key must be unique and stable.

4. Use useMemo and useCallback

React supports hooks as of version 16.8. Because functional components do not have shouldComponentUpdate, every call to a function component executes all its internal logic.

  • 1. Use useCallback to cache functions

A functional component needs to be refreshed every time its props or state changes. Some functions need not be recreated and should be cached.

import React, { useState } from 'react';
function Parent() {
    const [count, setCount] = useState(1);
    const [count2, setCount2] = useState(1);
 
    const callback = () => {
        return count;
    }
    return <>
    	<Child callback={callback}/>
        <button onClick={() => setCount(count + 1)}> count1+ </button>
        <button onClick={() => setCount(count2 + 1)}> count2+ </button>
	</>
}
 
function Child({ callback }) {
    
    const count = callback();
    return <div>{count}</div>
}
Copy the code

In the code above, when we click on count2+, the Parent component is updated and the callback is recreated, causing the Child component to be updated. But the callback remains the same and the Child should not be updated.

The optimization using useCallback is as follows

import React, { useState, useCallback } from 'react';
function Parent() {
    const [count, setCount] = useState(1);
    const [count2, setCount2] = useState(1);
 
    const callback = useCallback(() => {
        return count;
    }, [count])
    
    return <>
    	<Child callback={callback}/>
        <button onClick={() => setCount(count + 1)}> count1+ </button>
        <button onClick={() => setCount(count2 + 1)}> count2+ </button>
	</>
}
 
function Child({ callback }) {
    
    const count = callback();
    return <div>{count}</div>
}
Copy the code

After the optimization, when clicking count2+, the Parent component is updated, and since count remains the same, the callback will use the function created last time, so the Child will not be updated.

Usage scenario: Communication between components transfer functions.

    1. Optimization with useMemo

When we need to execute a function within a component, we want to recalculate only if the dependency changes and the result is the same if the dependency changes.

import React, { useState } from 'react';
function Parent() {
    const [count, setCount] = useState(1);
    const [count2, setCount2] = useState(1);
 
    const doubleCount = (() => {
        return count * 2;
    })()
    
    return <>
    	<p>{doubleCount}</p>
        <button onClick={() => setCount(count + 1)}> count1+ </button>
        <button onClick={() => setCount(count2 + 1)}> count2+ </button>
	</>
}
Copy the code

With useMemo, the computed value is cached, and when Parent updates, it is recalculated only when the dependency changes, otherwise the cached value is used.

import React, { useState, useMemo } from 'react';
function Parent() {
    const [count, setCount] = useState(1);
    const [count2, setCount2] = useState(1);
 
    const doubleCount = useMemo(() => {
        return count * 2;
    }, [count])
    
    return <>
    	<p>{doubleCount}</p>
        <button onClick={() => setCount(count + 1)}> count1+ </button>
        <button onClick={() => setCount(count2 + 1)}> count2+ </button>
	</>
}
Copy the code

For those interested, see how functional components can be optimized using the React.Memo cache component.

5. Optimization of functions used in JSX

Sometimes we use function declarations directly in JSX code to save trouble. This is simple, but the function is recreated every time the component is updated, causing the Child component to be re-rendered.

class Demo extends React.Component { constructor(props){ this.state = { count: Render () {return <> <Child callback={()=>{console.log('hello no bundle ')}}/> <button onClick={() => setCount(count +) 1)}> + </button> </> } }Copy the code

You can optimize to pass a reference to a function from the component so that the Child component is updated only when its props change.

class Demo extends React.Component { constructor(props){ this.state = { count: Render (){return <> <Child callback={this.handle}/> <button onClick={() => setCount(count + 1)}> + </button> </> } }Copy the code
6. Size up state

Put only states that require data-driven UI changes or require components to respond to its changes into state. Some students used to put all the states into state, which would cause some unnecessary rendering.

2. Vue

1. Use v-show and V-if properly in vUE

The essence of v-show is display: None, dom will still render. V-if is used to determine whether to render the DOM.

2. Avoid using v-for and V-if at the same time

Because v-for has a higher priority than V-if when Vue processes instructions, it means that V-IF will be repeated separately in each V-for loop. Try to process your data in advance, for example by using if logic in computed computations. f

2. Do not write long expressions or calculate data attributes in the template

Put some expressions or computations properly in Method or computed, and with computed, you can use caching to avoid repeated computations, and you can reuse code.

v-if="isShow && isAdmin && (a || b)" 
Copy the code
{count*2}
Copy the code
3. Use key

Like React, Vue decides whether to destroy and recreate the component or update it based on whether the key changes.

4. Reduce watch use

Watch can be used to monitor corresponding data changes and perform logical processing. The monitoring of Watch will be triggered when the monitored data changes. Therefore, when the data of Watch is large, consumption will be increased and performance will be affected.

If you use computed, you don’t use watch.

5. Use keep-alive to cache components

Sometimes you want to preserve the state of these components, and you can use keep-alive to cache components to avoid performance issues caused by repeated rerendering.

<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>
Copy the code

extension

Network for Front-end performance optimization

Refer to the link: www.ruanyifeng.com/blog/2018/0…

Welcome to pay attention to the author’s public account “code attack”