1. monorepo管理 #

1.1 MultiRepo #

1.1.1 优点 #

1.1.2 缺点 #

1.2 MonoRepo #

1.2.1 优点 #

1.2.2 缺点 #

1.3 使用lerna #

1.3.1 安装lerna #

npm i lerna -g

1.3.2 初始化项目 #

mkdir lerna-project
cd lerna-project

lerna init
lerna notice cli v4.0.0
lerna info Initializing Git repository
lerna info Creating package.json
lerna info Creating lerna.json
lerna info Creating packages directory
lerna success Initialized Lerna files

lerna-init

1.3.3 package.json #

package.json

{
  "name": "root",
  "private": true, // 私有的,用来管理整个项目,不会被发布到npm
  "devDependencies": {
    "lerna": "^4.0.0"
  }
}

1.3.4 lerna.json #

lerna.json

{
  "packages": [
    "packages/*"
  ],
  "version": "0.0.0"
}

1.4 yarn workspace #

1.4.1 开启workspace #

package.json

{
  "name": "root",
  "private": true, 
+  "workspaces": [
+    "packages/*"
+  ],
  "devDependencies": {
    "lerna": "^3.22.1"
  }
}

1.3.2 创建子项目 #

lerna create react
lerna create shared
lerna create scheduler
lerna create react-reconciler
lerna create react-dom

1.3.3 添加依赖 #

1.3.3.1 设置加速镜像 #
yarn config get registry
yarn config set registry http://registry.npm.taobao.org/
yarn config set registry http://registry.npmjs.org/

1.3.4 常用命令 #

1.3.4.1 根空间添加依赖 #
yarn add chalk --ignore-workspace-root-check
1.3.4.2 给某个项目添加依赖 #
yarn workspace react add object-assign
yarn install
lerna bootstrap --npm-client yarn --use-workspaces
1.3.4.4 其它命令 #
作用 命令
查看工作空间信息 yarn workspaces info
删除所有的 node_modules lerna clean 等于 yarn workspaces run clean
重新获取所有的 node_modules yarn install --force
查看缓存目录 yarn cache dir
清除本地缓存 yarn cache clean

2. 调试源码 #

2.1 下载代码 #

git clone https://gitee.com/mirrors/react.git

2.2 编译源码 #

cd react
yarn build react,shared,scheduler,react-reconciler,react-dom --type=NODE
cd build/node_modules/react
yarn link
cd build/node_modules/react-dom
yarn link
create-react-app debug-react17
cd debug-react17
yarn link react react-dom

3. setState的更新是同步还是异步的? #

3.1 setState #

3.2 异步更新 #

import * as React from 'react';
import * as ReactDOM from 'react-dom';

class Counter extends React.Component{
  state = {number:0}
  buttonClick = ()=>{
    console.log('buttonClick');
    this.setState({number:this.state.number+1});
    console.log(this.state.number);
    this.setState({number:this.state.number+1});
    console.log(this.state.number);
  }
  divClick = ()=>{
    console.log('divClick');
  }
  render(){
    return (
      <div onClick={this.divClick} id="counter">
        <p>{this.state.number}</p>
        <button onClick={this.buttonClick}>+</button>
      </div>
    )
  }
}
ReactDOM.render(<Counter/>,document.getElementById('root'));

3.3 回调执行 #

import * as React from 'react';
import * as ReactDOM from 'react-dom';

class Counter extends React.Component{
  state = {number:0}
  buttonClick = ()=>{
    console.log('buttonClick');
+   this.setState({number:this.state.number+1},()=>{
+     console.log(this.state.number);
+   }); 
+   this.setState({number:this.state.number+1},()=>{
+     console.log(this.state.number);
+   });
  }
  divClick = ()=>{
    console.log('divClick');
  }
  render(){
    return (
      <div onClick={this.divClick} id="counter">
        <p>{this.state.number}</p>
        <button onClick={this.buttonClick}>+</button>
      </div>
    )
  }
}
ReactDOM.render(<Counter/>,document.getElementById('root'));

3.4 函数更新 #

import * as React from 'react';
import * as ReactDOM from 'react-dom';

class Counter extends React.Component{
  state = {number:0}
  buttonClick = ()=>{
    console.log('buttonClick');
+   this.setState((state)=>({number:state.number+1}),()=>{
+     console.log(this.state.number);
+   });
+  
+   this.setState((state)=>({number:state.number+1}),()=>{
+     console.log(this.state.number);
+   });

  }
  divClick = ()=>{
    console.log('divClick');
  }
  render(){
    return (
      <div onClick={this.divClick} id="counter">
        <p>{this.state.number}</p>
        <button onClick={this.buttonClick}>+</button>
      </div>
    )
  }
}
ReactDOM.render(<Counter/>,document.getElementById('root'));

3.5 同步执行 #

import * as React from 'react';
import * as ReactDOM from 'react-dom';

class Counter extends React.Component{
  state = {number:0}
  buttonClick = ()=>{
    console.log('buttonClick');// 2234
    this.setState((state)=>({number:state.number+1}),()=>{
      console.log(this.state.number);
    });

    this.setState((state)=>({number:state.number+1}),()=>{
      console.log(this.state.number);
    });
+   setTimeout(()=>{
+     this.setState((state)=>({number:state.number+1}),()=>{
+       console.log(this.state.number);
+     });
+    
+     this.setState((state)=>({number:state.number+1}),()=>{
+       console.log(this.state.number);
+     });
    });

  }
  divClick = ()=>{
    console.log('divClick');
  }
  render(){
    return (
      <div onClick={this.divClick} id="counter">
        <p>{this.state.number}</p>
        <button onClick={this.buttonClick}>+</button>
      </div>
    )
  }
}
ReactDOM.render(<Counter/>,document.getElementById('root'));

3.6 强行批量更新 #

import * as React from 'react';
import * as ReactDOM from 'react-dom';

class Counter extends React.Component{
  state = {number:0}
  buttonClick = ()=>{
    console.log('buttonClick');// 2234
    setTimeout(()=>{
+     ReactDOM.unstable_batchedUpdates(()=>{
+       this.setState((state)=>({number:state.number+1}));
+       console.log(this.state.number);
+     });
    });
  }
  divClick = ()=>{
    console.log('divClick');
  }
  render(){
    return (
      <div onClick={this.divClick} id="counter">
        <p>{this.state.number}</p>
        <button onClick={this.buttonClick}>+</button>
      </div>
    )
  }
}
ReactDOM.render(<Counter/>,document.getElementById('root'));

3.7 并发更新 #

+ReactDOM.unstable_createRoot(document.getElementById('root')).render(<Counter/>);
+ReactDOM.createRoot(document.getElementById('root')).render(<Counter/>);

4.实现setState #

d6da53bd57c6623a994ea922a88becfe

4.1 src\index.js #

import { HostRoot, ClassComponent } from './ReactWorkTags';
import { Component } from './ReactFiberClassComponent';
import { NoMode, ConcurrentMode } from './ReactTypeOfMode';
import { batchedUpdates } from './ReactFiberWorkLoop';
class Counter extends Component {
  constructor() {
    super();
    this.state = { number: 0 };
  }
  onClick = (event) => {
    this.setState({ number: this.state.number + 1 });
    console.log('setState1', this.state.number);
    this.setState({ number: this.state.number + 1 });
    console.log('setState2', this.state.number);
    setTimeout(() => {
      this.setState({ number: this.state.number + 1 });
      console.log('setTimeout setState1', this.state.number);
      this.setState({ number: this.state.number + 1 });
      console.log('setTimeout setState2', this.state.number);
    });
  }
  render() {
    console.log('render', this.state.number);
    return this.state.number;
  }
}
let counterInstance = new Counter();
let mode = ConcurrentMode;
let rootFiber = { tag: HostRoot, updateQueue: [], mode }
let counterFiber = { tag: ClassComponent, updateQueue: [], mode }
counterFiber.stateNode = counterInstance;
counterInstance._reactInternals = counterFiber;
counterFiber.return = rootFiber;
rootFiber.child = counterFiber;
//合成事件
document.addEventListener('click',(nativeEvent)=>{
  let syntheticEvent = {nativeEvent};
  batchedUpdates(()=>counterInstance.onClick(syntheticEvent));
});
//1.同步不批量
//counterInstance.onClick();
//2.同步批量
//batchedUpdates(counterInstance.onClick);
//3.异步批量
//counterInstance.onClick();

4.2 src\ReactWorkTags.js #

src\ReactWorkTags.js

export const HostRoot = 3;
export const ClassComponent = 1;

4.3 src\ReactTypeOfMode.js #

src\ReactTypeOfMode.js

export const NoMode = 0b00000;
export const ConcurrentMode = 0b00100;

4.4 src\ReactFiberClassComponent.js #

src\ReactFiberClassComponent.js

import {scheduleUpdateOnFiber} from './ReactFiberWorkLoop';
let classComponentUpdater = {
    enqueueSetState: function (inst, payload) {
        const fiber = get(inst);
        const update = createUpdate();
        update.payload = payload;
        enqueueUpdate(fiber, update);
        scheduleUpdateOnFiber(fiber);
    }
}
function get(inst){
    return inst._reactInternals;
}
function createUpdate(){
    return {};
}
function enqueueUpdate(fiber, update) {
    var updateQueue = fiber.updateQueue;
    updateQueue.push(update);
}

export class Component {
    constructor() {
        this.updater = classComponentUpdater;
    }
    setState(partialState) {
        this.updater.enqueueSetState(this, partialState);
    }
}

4.5 src\ReactFiberWorkLoop.js #

src\ReactFiberWorkLoop.js


import { ClassComponent, HostRoot } from './ReactWorkTags';
import {NoMode,ConcurrentMode} from './ReactTypeOfMode'
let syncQueue;
let NoLanePriority = 0;
let SyncLanePriority = 12;
export let NoContext = 0;
export let BatchedContext = 1;
export let executionContext = NoContext;
function markUpdateLaneFromFiberToRoot(fiber) {
    let parent = fiber.return;
    while (parent) {
        fiber = parent;
        parent = parent.return;
    }
    if (fiber.tag === HostRoot) {
        return fiber;
    }
    return null;
}
export function getExecutionContext() {
    return executionContext;
}
export function batchedUpdates(fn) {
    const prevExecutionContext = executionContext;
    executionContext |= BatchedContext;
    try {
      return fn();
    } finally {
      executionContext = prevExecutionContext;
      if (executionContext === NoContext) {
        flushSyncCallbackQueue();
      }
    }
  }
export function scheduleUpdateOnFiber(fiber) {
    let root = markUpdateLaneFromFiberToRoot(fiber);
    ensureRootIsScheduled(root);
    if (executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode) {
        flushSyncCallbackQueue();
    }
}

function ensureRootIsScheduled(root) {
    let existingCallbackPriority = root.callbackPriority;
    let newCallbackPriority = SyncLanePriority;
    if (existingCallbackPriority === newCallbackPriority) {
        return;
    }
    scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
    queueMicrotask(flushSyncCallbackQueue);
    root.callbackPriority = newCallbackPriority;
}
export function flushSyncCallbackQueue() {
    if(syncQueue){
        for (let i = 0; i < syncQueue.length; i++) {
            let callback = syncQueue[i];
            do {
                callback = callback();
            } while (callback);
        }
        syncQueue = null;
    }
}
function scheduleSyncCallback(callback) {
    if (!syncQueue) {
        syncQueue = [callback];
    } else {
        syncQueue.push(callback);
    }
}
function performSyncWorkOnRoot(workInProgress) {
    let root = workInProgress;
    while (workInProgress) {
        if (workInProgress.tag === ClassComponent) {
            let inst = workInProgress.stateNode;
            inst.state = processUpdateQueue(inst,workInProgress);
            workInProgress.stateNode.render();
        }
        workInProgress = workInProgress.child;
    }
    commitRoot(root);
}
function processUpdateQueue(inst,workInProgress){
    return  workInProgress.updateQueue.reduce((state, update) => ({ ...state, ...update.payload }), inst.state);;
}
function commitRoot(root){
    root.callbackPriority = NoLanePriority;
}

5.React中的优先级 #

5.1 React中的优先级 #

前三者属于React的优先级机制,第四个属于scheduler的优先级机制

5.2 事件优先级 #

src\react\packages\shared\ReactTypes.js

export const DiscreteEvent = 0;
export const UserBlockingEvent = 1;
export const ContinuousEvent = 2;

src\react\packages\scheduler\src\Scheduler.js

function unstable_runWithPriority(priorityLevel, eventHandler) {
  switch (priorityLevel) {
    case ImmediatePriority:
    case UserBlockingPriority:
    case NormalPriority:
    case LowPriority:
    case IdlePriority:
      break;
    default:
      priorityLevel = NormalPriority;
  }
  var previousPriorityLevel = currentPriorityLevel;
  currentPriorityLevel = priorityLevel;
  try {
    return eventHandler();
  } finally {
    currentPriorityLevel = previousPriorityLevel;
  }
}

5.3 更新优先级 #

src\react\packages\react-reconciler\src\ReactFiberLane.js

const TotalLanes = 31;

export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;

export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /*                 */ 0b0000000000000000000000000000010;

export const InputDiscreteHydrationLane: Lane = /*      */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;

const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;

export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;

const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111110000000000000;

const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;

export const SomeRetryLane: Lanes = /*                  */ 0b0000010000000000000000000000000;

export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;

const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;

export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;

export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;

let currentUpdateLanePriority = NoLanePriority;

export function getCurrentUpdateLanePriority() {
  return currentUpdateLanePriority;
}

export function setCurrentUpdateLanePriority(newLanePriority) {
  currentUpdateLanePriority = newLanePriority;
}

5.4 任务优先级 #

src\react\packages\react-reconciler\src\ReactFiberLane.js


export const SyncLanePriority: LanePriority = 15;
export const SyncBatchedLanePriority: LanePriority = 14;

const InputDiscreteHydrationLanePriority: LanePriority = 13;
export const InputDiscreteLanePriority: LanePriority = 12;

const InputContinuousHydrationLanePriority: LanePriority = 11;
export const InputContinuousLanePriority: LanePriority = 10;

const DefaultHydrationLanePriority: LanePriority = 9;
export const DefaultLanePriority: LanePriority = 8;

const TransitionHydrationPriority: LanePriority = 7;
export const TransitionPriority: LanePriority = 6;

const RetryLanePriority: LanePriority = 5;

const SelectiveHydrationLanePriority: LanePriority = 4;

const IdleHydrationLanePriority: LanePriority = 3;
const IdleLanePriority: LanePriority = 2;

const OffscreenLanePriority: LanePriority = 1;

export const NoLanePriority: LanePriority = 0;

5.5 调度优先级 #

src\react\packages\react-reconciler\src\SchedulerWithReactIntegration.old.js

export const ImmediatePriority: ReactPriorityLevel = 99;
export const UserBlockingPriority: ReactPriorityLevel = 98;
export const NormalPriority: ReactPriorityLevel = 97;
export const LowPriority: ReactPriorityLevel = 96;
export const IdlePriority: ReactPriorityLevel = 95;
export const NoPriority: ReactPriorityLevel = 90;