Github Pines-Cheng/blog

React does have a unique approach to reducing repetitive rendering, the virtual DOM, but it’s clear React won’t be able to beat the speed of native rendering for the first time, or even beat other frameworks. Especially with React before optimization, render was executed every time data was changed, which significantly impacted performance, especially on the mobile end.

React default rendering behavior

Initialize render

When initializing the render, we need to render the entire application

(Green = rendered node)

Put forward the change

We want to update some of the data. These changes are only associated with one leaf node (green)

Ideal update

We just want to render these nodes on the critical path to the leaf node (green)

The default behavior

If you don’t tell React not to do this, it will (orange = wasteful rendering)

As you can see from the figure above, the component renders nodes that are not necessarily rendered in addition to the three nodes that are rendered, which is a big waste of performance. For complex pages, this can lead to a very poor overall experience. Therefore, to improve the performance of components, you should do everything possible to reduce unnecessary rendering.

React lifecycle

React’s life cycle is as follows for those of you who are not familiar with it.

shouldComponentUpdate

ShouldComponentUpdate is the key to optimization. The core of the React repeat optimization is to compare data in shouldComponentUpdate. Before optimization, shouldComponentUpdate returned true by default, which causes the Component to re-render whenever any data changes are triggered. This inevitably leads to wasted resources and poor performance — you may feel less responsive than native.

The key to React performance optimization isshouldComponentUpdate.

In the above example, because C2’s shouldComponentUpdate returns false, React does not need to generate a new virtual DOM and therefore does not need to update the DOM. Note that React doesn’t even need to call shouldComponentUpdate for C4 and C5.

C1 and C3 shouldComponentUpdate returns true, so React needs to go down to the leaf node to check them, C6 returns true because the virtual DOM is not equal and needs to update the DOM. Finally, of interest is C8. For this node, React needs to evaluate the virtual DOM, but because it’s equal to the old one, it doesn’t need to update the DOM.

React.PureComponent

If we pass in a component with only one props and state, we can simply use react. PureComponent, which will automatically shallow compare for us and control the return value of shouldComponentUpdate.

However, shallow-compare fails when more than one layer of props or state is passed, or when array and object are not. We can also use deepCopy and deepCompare in shouldComponentUpdate() to avoid unnecessary render(), but deepCopy and deepCompare are usually very expensive. In this case we need Immutable.

Immutable

Objects in JavaScript are generally Mutable. Because reference assignment is used, new objects simply refer to the original object, and changing new objects affects the original object. Such as

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

You’ll notice that foo.a is also changed to 2. While this can save memory, it is a very dangerous thing to do when the application is complex, and the merits of Mutable outweigh them. To solve this problem, it is common practice to use shallowCopy or deepCopy to avoid being modified, but this results in wasted CPU and memory.

Immutable is a good way to solve these problems.

What is Immutable Data

Immutable Data is Data that, once created, cannot be changed. Any modification or addition or deletion of an Immutable object returns a new Immutable object. Immutable is a Persistent Data Structure, which ensures that old Data can be used to create new Data without changing it. And to avoid the performance cost of deepCopy copying all nodes once, Immutable uses Structural Sharing, where if a node in the object tree changes, only that node and its affected parent are modified, and the other nodes are shared.

Check out this classic animation below:

immutable.js

Immutable. Js is essentially a JavaScript library for persistent data structures, but because React is popular at the same time and works seamlessly with React for performance optimizations, it’s common to combine the two.

Immutable. Js took Facebook engineer Lee Byron three years to build, but isn’t included in the React toolkit by default (React provides a simplified Helper). Its internal implementation of a complete set of Persistent Data Structure, and the Data Structure and methods are very rich (completely unlike JS origin good or not). Like Collection, List, Map, Set, Record, Seq. There are very comprehensive map, filter, groupBy, reduce, find function operation methods. Also, the API is as similar to Object or Array as possible. Immutable. Js is 16K compressed for download.

Here are three of the most important data structures :(Java programmers should be most familiar with them)

  • Map: a set of key-value pairs corresponding to Object. ES6 also has a dedicated Map Object

  • List: An ordered repeatable List, corresponding to an Array

  • Set: Unordered and non-repeatable list

A simple example

import { Map } from "immutable"; const map1 = Map({ a: 1, b: 2, c: 3 }); const map2 = map1.set('b', 50); map1.get('b'); // 2 map2.get('b'); / / 50Copy the code

seamless-immutable

Seamless -immutable is another library for Persistent Data structures that does not implement a complete Persistent Data Structure, Object.defineproperty (thus only available in IE9 and above) extends JavaScript’s Array and Object objects to support only Array and Object data types. The API is based on arrays and objects, so many don’t have to change their usage habits, and the intrusion into code is minimal. It also has a very small code base, only 2K downloads compressed.

A simple example

// import immutable from 'seamless-immutable' after using seamless-immutable. Var array = Immutable(["totally", "Immutable ", {hammer: "Can't Touch This"}]); array[1] = "I'm going to mutate you!" array[1] // "immutable" array[2].hammer = "hm, surely I can mutate this nested object..." Array [2]. Hammer // "Can't Touch This" for (var index in array) {console.log(array[index]); } // "totally" // "immutable" // { hammer: 'Can't Touch This'} JSON. Stringify (array) // '["totally","immutable",{"hammer":"Can't Touch This"}]'Copy the code

The seamless-immutable implementation relies on ECMAScript 5 features such as Object.defineProperty and Object.freeze, and therefore lacks browser compatibility:

This is not a problem, you can use Polyfill ES-shims/ES5-shim.

contrast

Although Immutable tries to be similar to native objects designed by the API, it can sometimes be difficult to tell Immutable from native, confusing operations.

Immutable Map and List correspond to native objects and arrays, but operate very differently, e.g. you use map.get(‘key’) instead of map.key, and array.get(0) instead of Array [0]. In addition, Immutable returns new objects every time it is modified, and it is easy to forget assignments.

When working with external libraries, you generally need to use native objects, and it’s easy to forget about transformations.

Of course, there are ways to avoid similar problems:

  • Use tools such as Flow or TypeScript that have static type checking

  • Convention for variable naming: For all Immutable objects, such as? At the beginning.

  • Create objects using Immutable. FromJS instead of Immutable.Map or Immutable.List to avoid mixing Immutable with native objects.

A fatal problem, however, is that using Immutable. Js is too expensive to modify existing code.

While seamless-immutable Data structures and apis are not as rich as immutable. Js, they are sufficient for those of us who just want to use immutable Data to optimize React to avoid repeated rendering. And Array and Object native methods can be used directly, the original project changes minimal.

Used in the React

Because seamless-immutable implementation relies on ECMAScript 5 and native Array and Object compatibility, it is very easy to use in React.

Initialize the state

When initializing state data, use the Immutable initialization method.

import Immutable from 'seamless-immutable';

state: {
    orderList: Immutable([]),
  }Copy the code

Modifying state Data

When modifying state data, also note:

saveOrderList(state, {payload: items}) { return {... state, orderList: Immutable(items)}; }Copy the code

shouldComponentUpdate

Using pure-render-decorator is convenient, fast, and elegant. Of course, since decorators are a feature of ES7, Babel needs to be configured itself.

import React from 'react';
import pureRender from 'pure-render-decorator';

@pureRender
class OrderListView extends React.Component {
  render() {
    const {orderList} = this.props;
    return (
      <div>
        {
          orderList.map((item) => {
            return (
              <div key={item.orderNum}>
                <div>{item.orderNum}</div>
                <div>{item.createTime}</div>
                <div>{item.contact}</div>
                <hr/>
              </div>
            );
          })
        }
      </div>
    );
  }
}

export default OrderListView;

Copy the code

React SCU optimizations are easy to implement.

reference

  • Performance optimisations for React applications

  • Immutable; React; Immutable

  • React Mobile Web extreme optimization