剖析React内部运行机制-结合源码分析“协调算法”执行过程

原文:Inside Fiber: in-depth overview of the new reconciliation algorithm in Reactreact

译文:React Fiber 那些事: 深刻解析新的协调算法算法


上面提供了两篇很是优秀的介绍React协调算法的文章,建议读者先看原文或者译文,看完一遍以后若是没有太深的印象,那么再从本文提供的角度去思考问题。segmentfault

新的协调算法

协调算法出现的背景

《剖析React内部运行机制-「译」React组件、元素和实例》 这篇文章中咱们知道:浏览器

  • React组件 即开发者所写的组件,在执行时会被React解析成 React元素
  • React元素 是描述组件实例或DOM节点及其所需属性的 普通对象
  • 不少不少 React元素 的层层嵌套最终造成了整个应用程序的 “树” 形结构。

那么如何将应用程序中众多的 “React元素” 组装成 就是协调算法核心工做的一部分。数据结构

注意: 浏览器只是React能够渲染的“渲染环境”之一,其余主要的目标是经过React native实现的iOS和Android视图(这就是为何“虚拟DOM”有点用词不当)。在 《剖析React内部运行机制-「译」React Fiber 架构》 这篇文章的 “协调与渲染” 部分有更加详细的介绍。架构

协调算法所使用的数据结构

React Fiber,其构造函数为:ide

function FiberNode(tag, pendingProps, key, mode) {
    // 此处属性用于生成DOM节点实例
    this.tag = tag; // 用于标识组件的类型,好比class组件、function组件、宿主组件等
    this.key = key;
    this.elementType = null;
    this.type = null;
    this.stateNode = null;
    
    // 此处属性用于协调算法遍历Fiber节点树
    this.return = null;
    this.child = null;
    this.sibling = null;
    this.index = 0;
    
    this.ref = null;
    
    this.pendingProps = pendingProps;
    this.memoizedProps = null;
    this.updateQueue = null;
    this.memoizedState = null;
    this.dependencies = null;
    
    this.mode = mode;
    
    // 此处属性用于反作用(的调用,好比生命周期函数等)
    this.effectTag = NoEffect;
    this.nextEffect = null;
    
    this.firstEffect = null;
    this.lastEffect = null;
    
    this.expirationTime = NoWork;
    this.childExpirationTime = NoWork;
    
    this.alternate = null;
    
    if (enableProfilerTimer) {
        // 下面的操做是为了不v8性能问题
        this.actualDuration = Number.NaN;
        this.actualStartTime = Number.NaN;
        this.selfBaseDuration = Number.NaN;
        this.treeBaseDuration = Number.NaN;
        this.actualDuration = 0;
        this.actualStartTime = -1;
        this.selfBaseDuration = 0;
        this.treeBaseDuration = 0;
    }
    
    {
        this._debugID = debugCounter++;
        this._debugSource = null;
        this._debugOwner = null;
        this._debugIsCurrentlyTiming = false;
        this._debugNeedsRemount = false;
        this._debugHookTypes = null;
        if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
            Object.preventExtensions(this);
        }
    }
}
复制代码

固然,React Fiber的属性用的最多的仍是前半部分。其更加详细做用在《剖析React内部运行机制-「译」React Fiber 架构》 这篇文章中有介绍。函数

经过程序看协调算法执行机制

一、协调算法入口函数-renderRootoop

function renderRoot(root, expirationTime, isSync) {
    ...
    // 该方法内部会建立workInProgress,并暴露到外层(即renderRoot)做用域
    prepareFreshStack(root, expirationTime);
    
    ...
    // 开始执行工做循环
    if (isSync) {
      workLoopSync();
    } else {
      workLoop();
    }
    
    ...
    // 检查工做循环结束后的workInProgress状态
    switch (workInProgressRootExitStatus) {
        ...
        case RootCompleted:
            return commitRoot.bind(null, root);
    }
}
复制代码

二、工做循环/组件解析-workLoopSyncpost

function workLoopSync() {
    while (workInProgress !== null) {
        workInProgress = performUnitOfWork(workInProgress);
    }
}
复制代码

三、执行工做单元-performUnitOfWork

function performUnitOfWork(unitOfWork) {
    var current$$1 = unitOfWork.alternate;
    
    ...
    
    var next = void 0;
    next = beginWork$$1(current$$1, unitOfWork, renderExpirationTime);
    
    ...
    
    return next;
}
复制代码

四、开始工做--beginWork$$1(其实是执行的是下面beginWork$1)

function beginWork$1(current$$1, workInProgress, renderExpirationTime) {
    
    ...
    
    // 这里根据当前组件的类型分别返回不一样的内容
    switch (workInProgress.tag) {
        ...
        
        case FunctionComponent:
            return updateFunctionComponent(current$$1, workInProgress, _Component, resolvedProps, renderExpirationTime);
        case ClassComponent:
            return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
        case HostRoot:
            return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
        case HostComponent:
            return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
        case HostText:
            return updateHostText(current$$1, workInProgress);
        
        ...
    }
}
复制代码

函数 beginWork 始终返回指向要在循环中处理的下一个子节点的指针或 null。

五、当匹配到ClassComponent

return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
复制代码

更新组件函数-updateClassComponent

function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
    
    ...
    
    var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime);
    
    return nextUnitOfWork;
}
复制代码

六、结束组件函数-finishClassComponent

function finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime) {
    
    ...
    
    var instance = workInProgress.stateNode;
    ...
    var nextChildren = void 0;
    ...
    // 这里会调用React组件里面的render函数获取返回值
    nextChildren = instance.render();
    ...
    // 协调算法的核心--查找child节点
    reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime)
    ...
    return workInProgress.child;
}
复制代码

七、协调算法核心之一-肯定child节点

function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
  if (current$$1 === null) {
    workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
  } else {
    workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
  }
}
复制代码

继续看下面函数的定义,了解workInProgress.child是被赋了什么值。

// mountChildFibers和reconcileChildFibers调用了同一个方法,分别是首次加载和后续更新时的执行逻辑
var reconcileChildFibers = ChildReconciler(true);
var mountChildFibers = ChildReconciler(false);

function ChildReconciler(shouldTrackSideEffects) {
    function deleteChild(returnFiber, childToDelete) {
        ...
    }
    
    ...
    // 协调单个元素(协调核心方法也会调用这里哦)
    function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {
        var key = element.key;
        var child = currentFirstChild;
        
        while (child !== null) {
            ...
            
            child = child.sibling;
        }
        
        if (element.type === REACT_FRAGMENT_TYPE) {
            // 这里经过函数名就能够知道,createFiberFromFragment将根据render函数返回值里面的Fragment元素建立Fiber对象。
            var created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key);
            created.return = returnFiber;
            // 天哪!终于有返回值了,是一个Fiber节点对象
            return created;
        } else {
            // createFiberFromElement将根据render函数返回值里面的DOM元素建立Fiber对象
            var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime);
            _created4.ref = coerceRef(returnFiber, currentFirstChild, element);
            _created4.return = returnFiber;
            // 返回一个Fiber节点对象
            return _created4;
        }
    }
    
    ...
    // 这里才是核心方法
    function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
    ...
    // 处理对象类型
    var isObject = typeof newChild === 'object' && newChild !== null;
    if (isObject) {
        switch (newChild.$$typeof) {
            case REACT_ELEMENT_TYPE:
                // 注意这里哦,会调用上面定义的函数
                return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime));
            case REACT_PORTAL_TYPE:
                return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime));
        }
    }
    ...
    return reconcileChildFibers;
}
复制代码

完成每件有意义的事情都会须要咱们不懈的坚持。 若是你坚持看到了这里,请为本身点个赞。

在React协调算法执行过程当中,会遇到不少不少的case,在这篇文章中咱们仅仅是对ClassComponent类型的组件抓住一条主线进行深刻的剖析。其余类型好比HostRootHostComponent等能够按照上述分析思路进行探索。

总结

文章开头提供的两篇文章对“协调算法”作了很好的理论分析,在理论分析的基础上,在React内部执行到renderRoot()函数内部时,经过逐级剖析源码中相关函数的主体(省略了不少判断以及校验等逻辑),分析了root对象上面的组件如何一步步被解析成对应的React元素树。

相关文章
相关标签/搜索