7
头图

In PR # React shortly before , the core member Brian Vaughn React some API and the internal flag .

One of the most noticeable changes is: React entrance of createRoot API .

The industry interprets this change as: Concurrent Mode (hereinafter referred to as CM ) will be stable in the near future and appear in the official version.

React17 is a transitional version to stabilize CM . Once CM stable, the progress of v18 will be greatly accelerated.

It can be said that from 18 to 21 years, the main work of the React CM out around 060b62f628be62, then:

  • CM is 060b62f628bed9?
  • What problem can CM React
  • CM not stable after almost 4 years, across 16 and 17 versions?

This article will answer.

What is CM

To understand what CM (concurrent mode) is, you first need to know the running process of the React

React can be roughly divided into two working phases:

  • render stage

In the render stage, the changed part of an update is calculated (through the diff algorithm), which is named after the render

render stage may asynchronous (depends on the trigger scenes updates).

  • commit stage

In commit stage will render required phase variation calculating section view rendering. Corresponding to ReactDOM appendChild , removeChild etc. will be executed.

commit phase must be called synchronously (so that the user will not see the incompletely rendered UI )

The application we ReactDOM.render belongs to the legacy mode.

In this mode, one render corresponds to one stage commit

If we pass ReactDOM.createRoot (not this current stable version API ) application created belong mentioned at the beginning CM ( concurrent mode)

Under CM , the update has the concept of priority, and the render phase may be interrupted by high-priority updates.

So the render stage may be repeated many times (restarted after being interrupted).

render stage may correspond to the commit stage multiple times.

In addition, there is a blocking mode for developers to slowly legacy mode to CM .

You can see the features supported by different modes feature comparison

不同模式支持的特性

Why do you need CM?

Knowing what CM is, then what is his use? Why does the React core team take more than 3 years (beginning in 18 years) to realize him?

This has to start React the design concept of 060b62f628c199.

We can from the official website React philosophy see React design concept:

We believe that React is the first choice to JavaScript build a fast response large Web

Among them, quick response is the focus.

So what affects rapid response to ? The answer given by the React

CPU bottlenecks and IO bottlenecks

CPU bottleneck

Consider the following demo , we render 3000 list items:

function App() {
  const len = 3000;
  return (
    <ul>
      {Array(len).fill(0).map((_, i) => <li>{i}</li>)}
    </ul>
  );
}

const rootEl = document.querySelector("#root");
ReactDOM.render(<App/>, rootEl);  

As mentioned earlier, in the legacy mode, the render stage will not be interrupted, so these 3000 li render must be completed in the same browser macro task.

Long-term calculations will block the thread and cause the page to drop frames. This is the bottleneck of CPU

The solution is: enable CM , change the render stage to to interrupt ,

When the remaining time of one frame of the browser is short, the control will be handed over to the browser. Wait for the free time of the next frame before continuing the component render .

IO bottleneck

loading caused by long-term calculations, the 060b62f628c360 state of the network request will also cause the page to be IO , which is the bottleneck of 060b62f628c362.

IO bottleneck of 060b62f628c37e is objective.

As a front end, all you can do is request the required data as soon as possible.

However, under normal circumstances: code maintainability and request efficiency are contradictory.

What do you mean, for example:

Suppose we encapsulate the method useFetch for requesting data, and distinguish whether the data is requested by whether the return value exists.

function App() {
  const data = useFetch();
  
  return {data ? <User data={data}/> : null};
}

In order to improve the maintainability , useFetch and the component User to be rendered exist in the same component App .

However, what if further data is required in the User profile data below)?

function User({data}) {
  const {id, name} = data?.id || {};
  const profile = useFetch(id);
  
  return (
    <div>
      <p>{name}</p>
      {profile ? <Profile data={profile} /> : null}
    </div>
  )
}

In the maintainability principle, useFetch with the component to be rendered Profile exist in the same assembly User in.

However, if the code is organized in this way, the Profile component can only wait for User render and then render .

Data can only flow down layer by layer like water in a waterfall.

This inefficient way of requesting data is called waterfall .

In order to improve request efficiency , we can "request Profile assembly operations required data" mentioned App inner assembly, incorporated useFetch in:

function App() {
  const data = useFetch();
  
  return {data ? <User data={data}/> : null};
}

But this reduces the maintainability (the Profile component is too far away from the profile data).

React team Relay from the experience of the Suspense feature to propose Server Components .

It is for the code maintainability and request efficiency when IO bottleneck.

The realization of this feature requires the CM update in have a different priority .

Why does CM take so long?

Next, from the source code, features, and ecology CM is to popularize 060b62f628c5d4 from the bottom up.

Source code level

Priority algorithm transformation

Before v16.13, React has realized the basic CM function.

As we talked about before, CM has the concept of update priority. Preceded by a number of milliseconds expirationTime numerals update expiration time.

  • Judge the priority by comparing different updated expirationTime
  • Judge whether the update has expired by comparing the updated expirationTime with the current time (expiration needs to be executed synchronously)

However, expirationTime as a time-related floating point number, cannot represent the concept of batch priority

In order to achieve the higher-level Server Components feature, the concept of batch priority

Therefore, the core member Andrew Clark began a long-term priority algorithm transformation, see: PR lanes

Offscreen support

In the meantime, another member of the Luna Ruan in the development of a new API - Offscreen .

It can be understood that this is the Keep-Alive feature React

Subscribe to external sources

CM is turned on, the following three life cycles will only be called once in one update:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

But open CM , due render stage may be interrupted, repeat, so they may be called multiple times.

When subscribing to external sources (such as registering event callbacks), the updates may not be timely or memory leaks.

For example: bindEvent is an publish and subscribe (such as a native DOM event):

class App {
  componentWillMount() {
    bindEvent('eventA', data => {
      thie.setState({data});
    });
  }
  componentWillUnmount() {
    bindEvent('eventA');
  }
  render() {
    return <Card data={this.state.data}/>;
  }
}

Bind in componentWillMount and unbind in componentWillUnmount .

When the event is received, update data .

When the render stage is repeatedly interrupted and suspended, it may appear:

Before the event is finally bound (before bindEvent executed), the event source triggered the event

At this time, the App component has not registered the event ( bindEvent not been executed yet), so the App obtained by data is the old one.

In order to solve this potential problem, the core member Brian Vaughn developed a feature: create-subscription

Used to standardize the subscription and update of external sources React

Simply put, the registration and update of external sources are bound to the state update mechanism of the component commit

Characteristic level

When the support at the source level of is complete, the CM will be on the agenda.

This is Suspense .

[[Umbrella] Releasing Suspense #13206] ( https://github.com/facebook/react/issues/13206), this PR is responsible for recording the progress of the Suspense

Umbrella tag means that this PR will affect a lot of libraries, components, and tools

It can be seen that the long timeline has been from 18 years to the last few days.

Initially Suspense was only front-end feature , at that time React SSR string data to the front end (also commonly known as dehydration)

Later React achieve a SSR assembly when streaming transmission protocol stream transmission component, rather than HTML string.

At this time, Suspense was given more responsibilities. It also has a more complex priority, which is one of the reasons why priority algorithm

The final result is the Server Components concept launched earlier this year.

Ecological level

When source level of supported, and feature is also developed, can it be seamlessly connected?

It's still early.

As a huge ship that has been running for 8 years, React is upgraded to the final community popularity, there is a huge amount of work to do in the middle.

In order to help the community slowly transition to CM , React done the following:

  • Develop the ScrictMode feature, and it is enabled by default, to standardize the developer's writing
  • Mark componentWillXXX unsafe , remind users not to use it, it will be discarded in the future
  • Proposed new life cycles ( getDerivedStateFromProps , getSnapshotBeforeUpdate ) to replace the life cycle that will be obsolete as above
  • Developed an legacy mode between CM transition- blocking mode

And this is just the simplest part

The hard part is:

How to migrate legacy model currently accumulated in the community?

The migration of many animation libraries and state management libraries (such as mobX ) is not easy.

to sum up

We introduced the CM and the difficulties of his migration.

Through this article, you must also know how React add createRoot (the way to turn on CM ) at the beginning.

Fortunately, everything is worth it. If React were in the past: the open source time was early and the community was large.

So starting from CM , React may be will be the most complex view frame in the front-end field.

By then, there will not be any React-like framework that can achieve the same React feature .

But some people say that CM are tasteless, and I don't need them at all.

What do you think of CM ? Welcome to leave your discussion.


卡颂
3.1k 声望16.7k 粉丝