7
头图

Hello everyone, I'm Casson.

Compared with 0621f1d08419df, Vue can be compiled based on the template. , React is a full runtime library, and can running.

Most of these optimizations are developers, but they often confuse developers when they perform performance optimization on projects. For example the following code:

function App {
  const [num, updateNum] = useState(0);
  console.log('App render', num);

  useEffect(() => {
    setInterval(() => {
      updateNum(1);
    }, 1000)
  }, [])

  return <Child/>;
}

function Child() {
  console.log('child render');
  return <span>child</span>;
}

How many pieces of information will be printed after the App component is mounted?

This article explains the performance optimization strategy React inside Demo

Online Demo address

Welcome to join human high-quality front-end framework group , with flying

The effect of performance optimization

If the optimization strategy is not considered, the code running logic is as follows:

  1. App component first time render , print App render 0
  2. child component Child first render , print child render
  3. After 1000ms, the setInterval callback is triggered and updateNum(1) executed
  4. App component again render , print App render 1
  5. child component Child again render , print child render
  6. Repeat steps 3 to 5 every 1000ms

In fact, we will find that repeating steps 3 to 5 will not produce any changes, and there is obviously room for optimization here.

For this situation, React did optimize. The above Demo will be printed in sequence:

  1. App render 0
  2. child render
  3. App render 1
  4. child render
  5. App render 1

The confusing point here is: why after num changes from 0 to 1, App render 1 is executed twice, while child render is executed only once?

Next, we explain the above reasons from the perspective of theoretical and actual .

Theory of performance optimization

A noun is mentioned in the useState document : bailout .

He means: when useState updated by state is the same as the current state of (using Object.is comparison), React will not be the descendant component render of component.

Note: After hitting bailout , the current component may still be render , but his descendant component will not be render .

This is because, in most cases, only the current components render and useState will be executed, so that state can be calculated and then compared with the current state of .

As far as our Demo is concerned, only App render and useState can be executed to calculate num :

function App {
  // useState执行后才能计算出num
  const [num, updateNum] = useState(0);
  // ...省略
}

In useState not bailing out when state does not change #14994 , Dan also reiterates this point.

So from a theoretical perspective, in our Demo in, num from the 1 to a 0, Child the render performed only once is understandable, because App hit bailout , then he subcomponents Child not render .

But bailout is only for the descendant component of the target component, so why for the target component App , App render 1 is not executed after 2 executions?

The actual performance optimization strategy is more complicated.

Practical performance optimization strategies

The workflow of React can be simply summarized as:

  1. Interactions (such as click events, useEffect ) trigger updates
  2. Component tree render

The bailout just mentioned occurs in step 2: after the component tree starts render , the descendant components of the component that hit bailout will not be render .

In fact, there is an optimization strategy that to : when step 1 triggers the update, it is found that state not changed, then step 2 will not be continued at all.

From our Demo :

function App {
  const [num, updateNum] = useState(0);
  console.log('App render', num);

  useEffect(() => {
    setInterval(() => {
      updateNum(1);
    }, 1000)
  }, [])

  return <Child/>;
}

Normally, updateNum(1) executes, triggering an update. The new num of will not be calculated until App render and useState executed, and then compared with the current num of to determine whether bailout is hit.

If updateNum(1) executed, the new num of is calculated immediately, and then compared with the current num of . If they are equal, the component tree will not be render .

This strategy of the timing of calculating state by , called eagerState (eager state ).

Summarize

To sum up, our Demo is the result of mixing these two optimization strategies:

  1. App render 0 (miss policy)
  2. child render
  3. App render 1 (miss policy)
  4. child render
  5. App render 1 (hit bailout )
  6. (hit eagerState )
  7. (hit eagerState )

......

For the implementation details of bailout , please refer to the React component render .

Due to limited space, the implementation details of eagerState will be discussed in a separate article.


卡颂
3.1k 声望16.7k 粉丝