react16+的渲染流程

230 阅读2分钟

设计理念

  • 跨平台
  • 快速响应:异步可中断+增量更新

react16+ 分为三个阶段

  • 调度
  • 调和
  • 提交

requestIdleCallback

可以使我们在主事件循环处理完的空余时间执行react任务,不影响关键事件

  • 1.react向浏览器申请时间片
  • 2.浏览器执行高优先级任务,如:渲染,布局,绘制,资源加载,事件响应,动画等
  • 3.浏览器把富裕时间片分配给react
  • 4.react执行每一个Fiber任务
  • 5.执行完每个Fiber任务,都会判断浏览器给的空余时间和是否有下一个Fiber任务,两个有一个不满足,就会把控制权让回给浏览器
let nextUnitOfWork=rootFiber;//下一个工作单元
let workInProgress=rootFiber;//正在执行的工作单元

function workLoop(deadline){
    let shouldYield=false;//是否有空余时间
    //判断是否执行
    while(nextUnitOfWork&&!shouldYield){
        nextUnitOfWork=performUnitOfWork(workInProgress);//执行当前工作单元,并且返回下一个工作单元
        shouldYield=deadline.timeRemaining()<1;//
    }

    //如果没有下一个执行单元,并且当前渲染树存在,进行提交,下面会在提交阶段讲
    if(!nextUnitOfWork&&workInProgress){
        commitRoot();
    }

    //循环调度,申请下一次
    requestIdleCallback(workLoop);
}

//调度阶段
requestIdleCallback(workLoop,{timeout:500});//

调和阶段

  • beginWork:把自元素变成Fiber对象

    • createDOM:创建真是DOM,更新属性
    • reconcilerChildren:创建Fiber对象,父亲与大儿子建立关系,儿子们建立关系
  • completeUnitOfWork:建立链表关系,通过单链表,firsEffect,nextEffect,lastEffect,将fiber链接起来

  • beginWork:

function performUnitOfWork(currentFiber){
    beginWork(currentFiber);

    //返回下一个工作单元,有儿子返回儿子,没儿子找叔叔,没叔叔,找白白
    if(currentFiber.child) return currentFiber.child
    while(currentFiber){
        completeUnitOfWork(currentFiber);
        if(currentFiber.sibling) return currentFiber.sibling;
        currentFiber=currentFiber.return;
    }
}

function beginWork(currentFiber){
    if(currentFiber.tag===TAG_ROOT){//根节点
        updateHostRoot(currentFiber);
    }else if(currentFiber.tag===TAG_HOST){//原生节点
        updateHost(currentFiber);
    }else if(currentFiber.tag===TAG_TXT){//文件节点
        updateTxtHost(currentFiber);
    }
}

//根节点
function updateHostRoot(currentFiber){
    const newChildren=currentFiber.props.children;
    reconcilerChildren(currentFiber,newChildren);
}

//原生节点
function updateHost(currentFiber){
    if(!currentFiber.stateNode){
        currentFiber.stateNode=createDOM(currentFiber);
    }
    const newChildren=currentFiber.props.children;
    reconcilerChildren(currentFiber,newChildren);
}

//文本节点
function updateTxtHost(currentFiber){
    if(!currentFiber.stateNode){
        currentFiber.stateNode=createDOM(currentFiber);
    }
}

//创建真是DOM,更新属性
function createDOM(currentFiber){
    if(currentFiber.type===ELEMENT_TXT){
        return document.creatTextNode(currentFiber.props.text);
    }
    const stateNode=document.createElement(currentFiber.type);
    updateProps(stateNode,{},currentFiber.props);
    return stateNode;
}

functiom reconcilerChildren(currentFiber,newChildren){
    let newChildrenIndex=0;//虚拟DOM数组的索引
    let prevSibing;
    while(newChildrenIndex < newChildrem.length){
        const newChild=newChildren[newChildrenIndex];//获取每一个虚拟DOM
        //设置不同的Fiber tag标示
        let tag;
        if(newChild&&newChild.type===ELEMENT_TXT){
            tag=TAG_TXT;
        }else if(newChild&&typeof newChild.type==='string'){
            tag=TAG_HOST;
        }

        let newFiber={
            tag,
            stateNode:null,
            return:currentFiber,
            effectTag:PLACEMENT,//PLACEMENT:更新,删除,移动,更细&移动
            nextEffect:null,
            type:newChild.type,
            props:newChild.props
        }

        //父亲与大儿子建立关系,儿子们之间建立关系
        if(newFiber){
            if(newChildIndex===0){//父亲的chid指向大儿子
                currentFiber.child=newFiber;
            }else{
                prevSibling.sibling=newFiber;//哥哥的sibling指向弟弟
            }
            prevSibling=newFiber;
        }
        newChildrenIndex++;
    }
}
  • completeUnitOfWork
function completeUnitOfWork(currentFiber){
    const returnFiber=currentFiber.return;
    if(returnFiber){
        //把自己的儿子们合并到父亲上
        if(!returnFiber.firstEffect){
            returnFiber.firstEffect=currentFiber.firstEffect;
        }
        if(currentFiber.lastEffect){
            if(returnFiber.lastEffect){//指向儿子的头
                returnFiber.lastEffect.nextEffect=currentFiber.firstEffect;
            }
            returnFiber.lastEffect=currentFiber.lastEffect;
        }

        //把自己合并的父亲上
        const effectTag=currentFiber.effectTag;
        if(effectTag){
            if(returnFiber.lastEffect){
                returnFiber.lastEffect.nextSibling=currentFiber;
            }else{//没有儿子的情况
                returnFiber.firstEffect=currentFiber;
            }
            returnFiber.lastEffect=currentFiber;
        }
    }
}

提交阶段

function commitRoot(){
    let currentFiber=workInProgress.firstEffect;
    while(currentFiber){
        commitWork(currentFiber);
        currentFiber=currentFiber.nextEffect;
    }
    workInProgress=null;
}

function commitWork(currentFiber){
    if(!currentFiber) return;
    const returnFiber=currentFiber.return;
    const parentNode=returnFiber.stateNode;
    if(currentFiber.effectTag===PLACEMENT&&currentFiber.stateNode!=null){
        parentNode.appendChild(currentFiber.stateNode);
    }
    currentFiber.effectTag=null;
}