After writing React for a while, I gradually became fond of using React to write applications.

We know that high performance was one of Facebook’s hallmarks when it launched React.

Today we will talk about performance optimizations for React and consider other ways to improve react performance and make react faster and better.

1. React component performance optimization (Render Angle optimization)

1. React Performance check tool

Before we talk about performance optimizations, we need to see how react components take time to load. Before React 16, we could use react Perf to check.

Install the React Perf extension in Chorme and add the corresponding code to the portal file or redux’s store.js:

React Perf

In the latest version of Act16, we can simply append the url with? React_pref, which can be performed in Chrome, we can view User Timeing to see the load time of the component.

React16.0 _pref


You can see the following figure for the specific operation of this tool:


React16.0 _pref. GIF

2. Optimize the performance of a single React component

2.1, reduce the number of new variables and bind functions in render as much as possible, and reduce the number of passed parameters as much as possible.

First let’s think about a question, for example, if I want to implement a button to increase the corresponding num by 1, what methods do we have?

Everyone should be able to think of no more than three, as shown below:

react_function

The first is to bind this in the constructor, the second is to bind this in the render() function, and the third is to use the arrow function.

But which method gives the best performance is a question to consider. You probably know the answer: the first one works best.

Because in the first case, the constructor is executed only once per render;

And the second way, every timerender()The function is re-executed;

The third way, every timerender()Will generate a new arrow function, even if the two arrow functions have the same content.

React is a shallow comparison to determine whether render is required. Simply speaking, it is determined by ===. If state or prop is a string or a number, it will be considered the same as long as the value is the same.

But if the former type is a complex object, and we know that the object is a reference type, shallow comparisons will only see these twopropIs it the same reference? If not, the two objects are considered to be different, even if their contents are identicalprop.

Here’s an example:

When we assign a prop named style to component Foo;

<Foo style={{ color:"red" }}
Copy the code

With this method, every render is considered a style and the prop is changed because each render generates an object to the style.

If you want react to render with the same object type as prop, you must make sure that prop points to the same javascript object.

const fooStyle = { color: "red" }; // Make sure this initialization is done only once, not in render, but in a constructor

<Foo style={fooStyle} />
Copy the code

This problem can be avoided in normal coding.

2.2. Customize shouldComponentUpdate

ShouldComponentUpdate is a function that determines when the React component should not be rerendered, but the default implementation of this function is simply to return true. That is, by default, every update calls the lifecycle functions used, including the render function, to re-render.

Let’s take a look at the following example

shouldComponentUpdate

We write two components, App and Demo, and write two methods, one to change the value of num in App and one to change the title. We print the render function in the render of Demo. We can see the following effects:

shouldComponentUpdate_demo

We can clearly see that although the title value in the demo component has not changed, it is still render.

To solve this problem, we can make the following changes to the Demo component:

shouldComponentUpdate

We will render only when the title value of the demo changes, and we can see the effect:

shouldComponentUpdate_demo

This is just a particularly simple customization of shouldComponentUpdate.

In the latest React, React provides the react.PureComponent. A plugin called react- Addons-pure-render -mixin was introduced earlier to re-implement the shouldComponentUpdate lifecycle method.

PureComponent

The effect of the above method is also consistent with our custom shouldComponentUpdate effect.

It is important to note, however, that the PureRender here is shallow comparison, because deep comparison scenarios are quite expensive. So we need to pay attention to some of the things we talked about in 1.1: Don’t set objects or arrays directly for props, and don’t bind methods directly to elements, because functions are objects

2.3, Immutable. Js

Start with a classic picture and a classic sentence:

Immutable

Shared mutable state is the root of all evil

— Pete Hunt

Objects in javascript are generally mutable because, with reference assignment, the new object simply refers to the original object, and changing the new object affects the original object.

Here’s an example:

foo = { a : 1 };
bar = foo;
bar.a = 2;
Copy the code

When we assign to bar.a, we find that foo.a also becomes 2. Although we can solve this problem by deep copy and shallow copy, this is very expensive and wasteful of CPU and memory.

Immutable Data, which is created Immutable, cannot be changed once it is created. Modifying, adding, or deleting an Immutable object returns a new Immutable object.

Here we look at three of the more important data structures

  • Map: set of key-value pairs, corresponding toObject.Es6There are also special speciesMapobject
  • List: Ordered repeatable List, corresponding toArray
  • ArraySet: An ordered and non-repeatable list

We can look at two examples:

Use Map to generate an IMmutable object

import { Map , is } from 'immutable';

let obj = Map({
  'name': 'react study'.'course': Map({name: 'react+redux'})})let obj1 = obj.set('name'.'darrell');

console.log(obj.get('course') === obj1.get('course')); / / return true
console.log(obj === obj1); / / returns false
Copy the code

Immutable. Is compares hashCode or valueOf of two objects (for JavaScript objects). Since immutable is stored internally using the Trie data structure, as long as the hashcodes of two objects are equal, the values are the same. This algorithm avoids deep traversal comparison and performs very well.

let obj = Map({name:1.title:'react'});
let obj1 = Map({name:1.title:'react'});
console.log(is(obj,obj1)); / / return true

let obj2 = {name:1.title:'react'};
let obj3 = {name:1.title:'react'};
console.log(is(obj2,obj3)); / / returns false
Copy the code

Immutable advantages:

  • Reduce memory usage
  • Concurrent security
  • Reduce project complexity
  • Easy to compare complex data, custom shouldComponentUpdate convenient
  • Time travel function
  • Functional programming

Immutable faults:

  • Learning costs
  • Library size (recommended to use seamless-immutable)
  • Serious intrusion into existing projects
  • Easy to confuse with native objects

If you want to know more, you can refer toimmutable,IMMUTABLE,.

2.4. Optimize performance of multiple React components and key

In the process of loading the React component, React generates a tree structure in memory by using the Render method. The nodes in the tree represent a React component or native Dom element. This tree structure is called the Vitural Dom.

React compares the original Vitural Dom and the newly generated Vitural Dom in the update phase, finds out the differences, and renders the Dom tree according to the differences.

React uses a time complexity of O(N) to compare two attribute structures in order to achieve high performance. To compare two tree structures, O(N^3) is required, which degrades performance.

Let’s give you a few examples that will make sense immediately:

  • Different Node types

    / / A component
    
    <div>
      <Todos />
    </div>
    
    / / B component
    <span>
      <Todos />
    </span>
    Copy the code

    We want to update component A to component B. When react is compared, we find that the outer root of the

    node is not the same. This is A huge waste of time, but in order to avoid O(N^3) time complexity, we can only use this method

    Therefore, during the development process, we should try to avoid the above situation, do not arbitrarily change the type of wrapped nodes.

  • The two nodes are of the same type

    There are two cases where the node is of Dom type and there is a React component.

    For dom types, let’s take an example:

    / / A component
    <div style={{color: 'red'.fontSize:15}} className="welcome">
      Hello World!!!
    </div>
    
    / / B component
    <div style={{color: 'green'.fontSize:15}} className="react">
      Good Bye!!!
    </div>
    Copy the code

    The difference between A and B components above is that the color in the text, className, and style changes. Because the Dom element does not change, React only changes the parts it changes.

    ShouldComponentUpdate (shouldComponentUpdate, shouldComponentUpdate, shouldComponentUpdate, shouldComponentUpdate)

  • Multiple child components

    Let’s look at two examples

    Example 1:

    // A
    <ul>
      <TodoItem text="First" complete={false} />
      <TodoItem text="Second" complete={false} />
    </ul>
    
    // B
    <ul>
      <TodoItem text="First" complete={false} />
      <TodoItem text="Second" complete={false} />
      <TodoItem text="Third" complete={false} />
    </ul>
    Copy the code

    To change from A to B, if shouldComponentUpdate is handled properly, we only need to update the time that third was loaded.

    Let’s look at the next example:

    // A
    <ul>
      <TodoItem text="First" complete={false} />
      <TodoItem text="Second" complete={false} />
    </ul>
    
    // B
    <ul>
      <TodoItem text="Zero" complete={false} />
      <TodoItem text="First" complete={false} />
      <TodoItem text="Second" complete={false} />
    </ul>
    Copy the code

    React adopts O(n) time complexity, so it changes text First to Zero, text Second to First, and adds another component at the end, text Second. The existing two text properties have been changed so they are rendered in sequence.

    If we have 1000 instances here, then 1000 updates will happen.

    That’s what we’re going to use hereKeythe

    This Key is the react component’s id number.

    React will know that the second and third components in B are actually the first and second instances in A.

    // A
    <ul>
      <TodoItem key={1} text="First" complete={false} />
      <TodoItem key={2} text="Second" complete={false} />
    </ul>
    
    // B
    <ul>
      <TodoItem key={0} text="Zero" complete={false} />
      <TodoItem key={1} text="First" complete={false} />
      <TodoItem key={2} text="Second" complete={false} />
    </ul>
    Copy the code

    But now react also reminds us not to forget to use the key. If we don’t add the key, we’ll get an error in the browser.

    react_key

    The important thing about using a key is that the key value should be stable, just as the id number is stable for us.

    A common mistake is to use the subscript of an array as a key. This can be dangerous.

    <ul>
      {
            todos.map((item, index) => {
                <TodoItem
                  key={index}
                  text={item.text}
                  completed={item.completed}
            })
      }
    </ul>
    Copy the code

Redux Performance Optimization: ResELECT

In the previous optimization process, we were optimizing rendering to improve performance, sincereactandreduxThe rendering process is driven by data, so the process of optimizing the rendering process and obtaining data is also an optimization point that needs to be considered.

// Here is a simple filter in Redux
const getVisibleTodos = (todos, filter) = > {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t= > t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t= >! t.completed) } }const mapStateToProps = (state) = > {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}
Copy the code

The mapStateToProps function is an important part of getting the data in the Redux Store. When we display the items in filter and toDOS, we traverse the array of toDOS fields.

When the array is large, it degrades performance.

This is where ResELECT comes in, and it works by using the last cached result as long as the relevant state hasn’t changed.

I will not introduce the specific usage here too much, there are a lot of great people have written about it, I will not repeat, if you want to know more about it, you can refer to ResELECT giuhub, Redux middleware – ResELECT.

Three: reference materials

This article is based on the chapters on how to optimize React performance in the Books “Inside the React Stack” and “Inside the React and Redux”, as well as my own hands-on practice and thinking.

There will be some mistakes in the article and I would like to invite your criticism and correction.





Author: Darrell


Link: https://www.jianshu.com/p/333f390f2e84


Source: Jane Book


Brief book copyright belongs to the author, any form of reprint please contact the author to obtain authorization and indicate the source.