React performance optimization

preface
React is a class library for building user interface developed by Facebook. It focuses on performance from the beginning of design, and can take some strategies when using it. Then our website performance is more optimized. The following are some optimization methods I usually use, hoping to help you!

Code Splitting
Code Splitting can help you "lazy load" code. If you can't directly reduce the volume of the application, you might as well try to split the application from a single bundle into a single bundle + multiple copies of dynamic code. Webpack provides three code separation methods. See the official website of webpack for details

1. Entry starting point: use entry configuration to manually separate codes.

2. Prevent repetition: use SplitChunks to remove and separate chunks.

3. Dynamic import: it is used to separate the code through the inline function call of the module.

Here, let's learn about the third dynamic import method.

1. For example, you can import

import { add } from './math';
console.log(add(16, 26));
Copy code

Rewrite it to the form of dynamic import, so that the math module is not loaded for the first time, so as to reduce the volume of resources loaded for the first time.

import("./math").then(math => {
  console.log(math.add(16, 26));
});
Copy code

2. For example, the high-order component of react, react loadable, is referenced for dynamic import.

import Loadable from 'react-loadable';
import Loading from './loading-component';

const LoadableComponent = Loadable({
  loader: () => import('./my-component'),
  loading: Loading,
});

export default class App extends React.Component {
  render() {
    return <LoadableComponent/>;
  }
}
Copy code

When the above code is loaded for the first time, it will first display a loading component, and then dynamically load the code of my component. After the component code is loaded, it will replace the loading component

shouldComponentUpdate avoid duplicate rendering
When the props or state of a component changes, React determines whether it is necessary to update the actual DOM by comparing the newly returned element with the previously rendered element. When they are not equal, React updates the dom.

In some cases, your component can speed up by rewriting the lifecycle function shouldComponentUpdate, which is triggered before the re rendering process begins. This function returns true by default to enable React to perform updates.

In order to further illustrate the problem, the figure on the official website is cited for explanation, as shown in the following figure (SCU indicates shouldComponentUpdate, green indicates return true (need to be updated), red indicates return false (no need to be updated); vDOMEq indicates virtual DOM comparison, green indicates consistency (need not be updated), and red indicates change (need to be updated))

According to the rendering process, we will first determine whether shouldComponentUpdate(SCU) needs to be updated. If it needs to be updated, call the render of the component to generate a new virtual DOM, and then compare it with the old virtual DOM (vDOMEq). If the comparison is consistent, it will not be updated. If the comparison is different, the DOM will be updated according to the minimum granularity change; If the SCU does not need to be updated, it will remain unchanged directly, and its child elements will remain unchanged.

C1 root node, green SCU and red vDOMEq, indicates that it needs to be updated.

C2 node, red SCU, indicates that it does not need to be updated. At the same time, C4 and C5 as their child nodes do not need to check for updates.

C3 node, green SCU and red vDOMEq, indicates that it needs to be updated.

C6 node, green SCU and red vDOMEq, indicates that it needs to be updated.

C7 node, red SCU, indicates that it does not need to be updated.

C8 node, green SCU, indicates that React needs to render this component;

Green vDOMEq indicates that the virtual DOM is consistent and the DOM is not updated.

Therefore, we can overload shouldComponentUpdate according to our business characteristics and return true only when we confirm that the real DOM needs to be changed. The general method is to compare whether the props and state of the component have really changed. If so, return true, otherwise return false. Cite the case on the official website.

class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}
Copy code

In the above code, shouldComponentUpdate only checks for changes in props.color and state.count. If these values do not change, the component will not be updated. When your component becomes more complex, you can use a similar pattern to make a "shallow comparison" to compare attributes and values to determine whether you need to update the component. This pattern is very common, so React provides a helper object to implement this logic - inherited from React.PureComponent.

In most cases, you can use React.PureComponent instead of writing your own shouldComponentUpdate, which only makes a shallow comparison. However, when the target of your comparison is reference type data, shallow comparison will ignore attribute or state mutation. At this time, you can't use it. At this time, you need to pay attention to the following non mutation data.

Attachment: Data mutated means that the reference of the variable has not changed (the pointer address has not changed), but the data pointed to by the reference has changed (the data pointed to by the pointer has changed). For example, const x = {foo: 'foo'}. x.foo = 'none' is a mutation.

Use non mutatible data structures
Use the examples on the official website to explain the problems caused by mutation data. For example, suppose you want a ListOfWords component to render a comma separated word list, and use a parent component named WordAdder with a click button to add a word to the child list. The following code is incorrect:

class ListOfWords extends React.PureComponent {
  render() {
    return <div>{this.props.words.join(',')}</div>;
  }
}

class WordAdder extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      words: ['marklar']
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // This will cause the code not to run as you expect
    const words = this.state.words;
    words.push('marklar');
    this.setState({words: words});
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick} />
        <ListOfWords words={this.state.words} />
      </div>
    );
  }
}
Copy code

The reason why the code can't work properly is that PureComponent only makes a "shallow comparison" between the old and new values of this.props.words. After the words value is modified in handleClick, even if a new word is added to the array, the old and new values of this.props.words are the same when compared (reference object comparison), so ListOfWords will not be rendered. The easiest way to avoid such problems is to avoid using attributes or states whose values may mutate, such as:

1. concat is used for arrays and Object.assign() is used for objects

handleClick() {
  this.setState(prevState => ({
    words: prevState.words.concat(['marklar'])
  }));
}
Copy code
// Suppose we have an object called colormap, and the following method does not pollute the original object
function updateColorMap(colormap) {
  return Object.assign({}, colormap, {right: 'blue'});
}
Copy code

2. ES6 supports the spread syntax of arrays or objects

handleClick() {
  this.setState(prevState => ({
    words: [...prevState.words, 'marklar'],
  }));
};
Copy code
function updateColorMap(colormap) {
  return {...colormap, right: 'blue'};
}
Copy code

3. immutable.js using non mutatible data

immutable.js makes change tracking easy. Each change will result in a new object, so we just need to check whether the index object has changed.

const SomeRecord = Immutable.Record({ foo: null });
const x = new SomeRecord({ foo: 'bar' });
const y = x.set('foo', 'baz');
x === y; // false
 Copy code

In this example, a new index is returned after x mutation, so we can safely confirm that x has been changed. The non mutative data structure helps us easily track object changes, so that we can quickly implement shouldComponentUpdate.

Components shall be split and decoupled as much as possible
Components should be subdivided as much as possible. For example, an input+list component can divide the list into a PureComponent, which is updated only when the list data changes. Otherwise, when the input value changes and the page is re rendered, the list also needs unnecessary DOM diff.

List class component optimization
The key attribute provides another way of component identification outside the component class. Through the key ID, when components are added, deleted, modified, sorted, etc., the DOM order can be directly adjusted according to the position of the key value, telling React to avoid unnecessary rendering and performance waste.

var items = sortBy(this.state.sortingAlgorithm, this.props.items);
return items.map(function(item){
  return <img src={item.src} />
});
Copy code

When the order changes, React will diff the elements and change the src attribute of img. It shows that such operation efficiency is very low. At this time, we can add a key attribute to the component to uniquely identify the component:

return
Copy code
After adding a key, React will not diff, but directly use the insertBefore operation to move the component location, which is the most efficient way to move DOM nodes.

bind function
There are three ways to bind this:

1. constructor binding

constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this); //Binding in constructor
}
//Then you can
<p onClick={this.handleClick}>
Copy code

2. Bind when using

<p onClick={this.handleClick.bind(this)}>
Copy code

3. Use arrow function

<Test click={() => { this.handleClick() }}/>
Copy code

The above three methods,

The first is optimal. Because the first constructor is executed only once when the component is initialized.

The second component executes every render.

The third method generates a new arrow function every time you render. Example: the click attribute of the Test component is an arrow function. When the component is re rendered, the Test component will be updated because of the newly generated arrow function, resulting in unnecessary rendering of the Test component.

Don't abuse props
props try to transfer only the required data, avoid redundant updates, and try to avoid using {... props}

ReactDOMServer for server-side rendering components
In order that users can see the fully rendered page more quickly, you can use the server-side rendering technology. Here, learn about ReactDOMServer.

In order to implement SSR, you may use nodejs framework (Express, Hapi, Koa) to start a web server, then call renderToString method to render your root component into a string, and finally output it to response.

// using Express
import { renderToString } from "react-dom/server";
import MyPage from "./MyPage";
app.get("/", (req, res) => {
  res.write("<!DOCTYPE html><html><head><title>My Page</title></head><body>");
  res.write("<div id='content'>");  
  res.write(renderToString(<MyPage/>));
  res.write("</div></body></html>");
  res.end();
});
Copy code

The client uses the render method to generate HTML

import ReactDOM from 'react-dom';
import MyPage from "./MyPage";
ReactDOM.render(, document.getElementById('app'));
Copy code
react performance testing tool
Before react16, we can use the react addons perf tool to view it. In the latest version 16, we only need to add "add" after the url? react_pref.

First, let's take a look at react addons perf. React addons perf is a Performance Toolkit officially launched by react, which can print the time, times and waste of time of component rendering. To briefly describe several APIs, please refer to the official website for specific usage:

Perf.start() starts recording

Perf.stop() end record

Perf.printInclusive() view the render of all designed components

Perf.printWasted() view unwanted waste component render

Let's take a look at the react16 version of the method. Add the url after the url? react_pref, you can view the loading time of components by viewing user timing in the performance of Chrome browser. Click record to start recording. Note that the recording duration should not exceed 20s, otherwise chrome may hang.

last
If you think this article is a little helpful to you, give it a compliment. Or you can join my development exchange group: 1025263163 learn from each other, and we will have professional technical Q & A to solve doubts

If you think this article is useful to you, please click star: https://gitee.com/ZhongBangKeJi/CRMEB esteem it a favor!

Keywords: Javascript node.js crmeb

Added by mzshah on Tue, 23 Nov 2021 05:37:38 +0200