React运行时

173 阅读2分钟

React 运行时

React.render(<App />, div)

  • 创建fiberRoot 和 rootFiber

rootFiber 作为fiber树的根节点,它的处理是具有特殊性的
// 这个函数功能就是一句话:根据组件的类型做不同的渲染处理

function beginWork(current, workInProgress, renderLanes) {
    // 跳过更新的条件(是否存在过期时间<执行了setState会生成>  新旧props ===)
    case FunctionComponent: 
    case ClassComponent: 
    case HostRoot:
    case HostComponent:
    case ForwardRef:
    case Fragment:
    case ContextProvider:
    case ContextConsumer:
}

对 rootFiber 的处理

function updateHostRoot(current, workInProgress, renderLanes) {
    // 1. rootFiber 的children其实就是 <App />,关于标签的本质,在我之前的文章有描述;
    // 2. reconcileChildren(current, workInProgress, nextChildren) 解析children
  
    // 3. 返回的 child(已经被reconcileChildren处理成fiber对象) 
    // 继续做beginwork根据组件类型渲染,其实是一个递归的过程;
    return workInProgress.child;
}

reconcileChildren 做了些啥?

function reconcileChildren(returnFiber, currentFirstChild, newChild) {

    // 这里分成3个部分

    // 1. 如果是普通的 REACT_ELEMENT_TYPE 组件,直接返回fiber对象,这个fiber对象的创建依据如下:
    // 根据被babel编译的标签返回的对象;
    // 复用上一次的fiber(这个发生在更新阶段),复用组件上次的状态, 但会传入pendingprops(因为每次标签都会执行 React.createElement); 
    // 复用的条件: 
    // - key; 
    // - type(elementType) 这个其实就是你定义的组件, 需要 === 才能判断一致;
    // 在函数组件里 const 一个组件,这个时候注意了,在你每次更新进来时,都会重新 const 新的组件,会影响diff和组件的复用,建议组件抽到外面去;

    // 2. diff
    // newChild.type === REACT_FRAGMENT_TYPE  <></>
    // newChild = newChild.props.children
    // reconcileChildrenArray, diff过程


    // 3. 删除多余的组件
    return deleteRemainingChildren(returnFiber, currentFirstChild);
}

直到遍历到单侧最底, 又开始从底向上遍历

function completeUnitOfWork(unitOfWork) {
    // 对于底层组件
    // 1. 如果是 hostComponent 即原生浏览器标签, 创建dom,css属性添加,事件绑定
    // 2. appendChild 子节点
    // 2. 组件的 context 的pop(维护context, 使每个组件只能使用祖先的context)
    // 4. 向上遍历并传递副作用(effect)
    // 5. 如果这个fiber存在sibling,会继续beginWork递归操作
}

最终所有副作用都在root上;
此时,dom都渲染了,开始执行这些副作用;
dom更新或者删除,生命周期