React源码解析6-类组件的更新

475 阅读4分钟

1.路口方法

beginWork()

判断组件更新是否可以优化

根据节点类型分发处理不同更新逻辑

根据expirationTime等信息判断节点是否可以跳过

2.tag判断不同方法

1.函数组件

直接传入props 拿到children的jsx 转成React.createElement元素 再去调用reconcileChildren

reconcileChildren

1.根据props.children生成fiber子树

2.判断fiber对象是否可复用

3.列表根据key优化

3.一般组件更新流程

image-20210507094249300

4.class组件的更新

image-20210507095834156

1.在mountIntance时候会执行willMount

2.组件刚开始挂载时候是采用子组件先挂载原则的 我们创建时候只会创建instance 不会创建current(组件的fiber 只有执行完render函数后才会有current) 我们判断父组件有没有current来执行子组件是挂载还是更新,这样的顺序 父先有instance 子有instance和fiber 子挂载有current 父挂载有current的顺序。

3.挂载之后(这时候已经有页面啦)再去执行didmount 这个生命周期 如果有setstate则会触发更新 创建一个update payload设为新的state 加入到这个class的fiber queue中 如果有多个update 那么会在updateQueue中以basestate:{number:0} 然后firstUpdate->secondUpdate形式存放

然后走到schdulework 然后找到root优先级最高的去执行更新 走到requestwork 发现还在rendering(因为在didmont时候就是还在render(挂载)阶段 这时候直接返回)

等挂载阶段完成后 会在commit lifeCycle时候去执行schdulework 然后performSyncwork 然后执行updateClasssComponent带上updateQueue 当作一次更新啦

4.5.生命周期的更新流程

image-20210507113502388

5.生命周期因为一开始在render阶段所以会先收集state 而不是立即去执行performwork 事件触发一开始batch阶段 所以一开始也不会执行performwork

都是先加到queue中

都是后面再来执行performwork去更新class组件 执行updateQueue中的所有queue

6.异步函数修改state都是一开始只会把回调函数记录到callback中 ,先同步代码执行完更新完视图(步骤和之前一样) 后面再去执行异步 异步代码加入会修改state 再进入requestwork来更新state 此时不是batch状态啦 只有直接在事件函数里面的才是batch 这个时候就直接performSyncwork更新视图

所以异步函数里面都是一个setstate走一次performwork 有一个updateQueue 有一个baseState 而不会两个setState连在一个queue中共享一个baseState.

handleClick=(e)=>{
    //baseState都是一样的 他们是同一个updateQueue更新
    this.setState({number:this.state.number+1})
     this.setState({number:this.state.number+1})
     //下面的setTimeout 不走batch 所以就是多个performwork 多个updateQueue 多个baseState 
     setTimeout(()=>{
       this.setState({
         number:this.state.number+1
       })
       console.log('state',this.state.number);
       this.setState({
         number:this.state.number+1
       })
       console.log('state',this.state.number);
     },1000)
  }

5.nextUnitofWork

是从root开始一直往下找可以更新的节点 ,所有节点都走到beginwork 判断该节点是否要更新 只有需要更新的节点才去更新 不需要更新直接返回啦

怎么判断是否需要更新 通过props和当前任务的expirationtime和你这个元素的expirationtime比较

        if (oldProps === newProps && !hasContextChanged() && ((updateExpirationTime === NoWork ||//!没跟新
        updateExpirationTime > renderExpirationTime))//!或者当前渲染有优先级 不执行该节点更新

这个是不需要更新的 因为你的updateExpirationTime是0则你不需要更新 或者你优先级低你这个元素此时也不需要更新

或者你的props没变化

只要一个满足了该节点就需要更新 比如

class app{
 render(){
 return <div>{{this.state.number}}</div>
 }
}


这里面number变化啦 props就会变化 则这个div要更新props

而app 是因为updateExpirationTime===renderExpirationTime因为此时你的任务刚好是改变这个app的state 所以你的renderExpirationTime也是app的,然后判断到app元素时候发现相等则也需要更新这个app 这里面执行逻辑就是更新组件的updateQueue

6.第一次dom挂载

updateContainerAtExpirationTime->schedulework->schedulework->requestwork->performsyncwork->performwork->renderRoot->workloop->performunitwork-这时候的unitwork就是根元素 他的updateQueue的update的payload就是他的子节点image-20210507152203414

->beginwork(此时发现 updateExpirationTime > renderExpirationTime)表示该根节点需要更新 因为本来就是根节点要挂载,不会直接return

->updateHostRoot(这个方法拿出updateQueue,执行processQueue)之后的state就变成啦子元素 nextchidren=nextState.element 然后reconcilechildren 构建新children的fiber树

这个是构建fiber是父优先 但是挂载元素是子优先 后续讲到commit阶段 再看怎么把这个fiber树变成真实dom树的