最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React原理解析第一篇:核心概念

    正文概述 掘金(Axizs)   2021-01-13   311

    作为一个构建用户界面的库,React的核心始终围绕着更新这一个重要的目标,将更新和极致的用户体验结合起来是React团队一直在努力的事情。为什么React可以将用户体验做到这么好?我想这是基于以下两点原因:

    • Fiber架构和Scheduler出色的调度模式可以实现异步可中断的更新行为。
    • 优先级机制贯穿更新的整个周期

    本文是对React原理解读系列的第一篇文章,在正式开始之前,我们先基于这两点展开介绍,以便对一些概念可以先有个基础认知。

    Fiber是什么

    Fiber是什么?它是React的最小工作单元,在React的世界中,一切都可以是组件。在普通的HTML页面上,人为地将多个DOM元素整合在一起可以组成一个组件,HTML标签可以是组件(HostComponent),普通的文本节点也可以是组件(HostText)。每一个组件就对应着一个fiber节点,许多个fiber节点互相嵌套、关联,就组成了fiber树,正如下面表示的Fiber树和DOM的关系一样:

        Fiber树                    DOM树
    
       div#root                  div#root
          |                         |
        <App/>                     div
          |                       /   \
         div                     p     a
        /   ↖
       /      ↖
      p ----> <Child/>
                 |
                 a
    
    

    一个DOM节点一定对应着一个Fiber节点,但一个Fiber节点却不一定有对应的DOM节点。

    fiber 作为工作单元它的结构如下:

    function FiberNode(
      tag: WorkTag,
      pendingProps: mixed,
      key: null | string,
      mode: TypeOfMode,
    ) {
    
      // Fiber元素的静态属性相关
      this.tag = tag;
      this.key = key; // fiber的key
      this.elementType = null;
      this.type = null; // fiber对应的DOM元素的标签类型,div、p...
      this.stateNode = null; // fiber的实例,类组件场景下,是组件的类,HostComponent场景,是dom元素
    
      // Fiber 链表相关
      this.return = null; // 指向父级fiber
      this.child = null; // 指向子fiber
      this.sibling = null; // 同级兄弟fiber
      this.index = 0;
    
      this.ref = null; // ref相关
    
      // Fiber更新相关
      this.pendingProps = pendingProps;
      this.memoizedProps = null;
      this.updateQueue = null; // 存储update的链表
      this.memoizedState = null; // 类组件存储fiber的状态,函数组件存储hooks链表
      this.dependencies = null;
    
      this.mode = mode;
    
      // Effects
      // flags原为effectTag,表示当前这个fiber节点变化的类型:增、删、改
      this.flags = NoFlags;
      this.nextEffect = null;
    
      // effect链相关,也就是那些需要更新的fiber节点
      this.firstEffect = null;
      this.lastEffect = null;
    
      this.lanes = NoLanes; // 该fiber中的优先级,它可以判断当前节点是否需要更新
      this.childLanes = NoLanes;// 子树中的优先级,它可以判断当前节点的子树是否需要更新
    
      /*
      * 可以看成是workInProgress(或current)树中的和它一样的节点,
      * 可以通过这个字段是否为null判断当前这个fiber处在更新还是创建过程
      * */
      this.alternate = null;
    
    }
    
    

    fiber架构下的React是如何更新的

    首先要明白,React要完成一次更新分为两个阶段: render阶段和commit阶段,两个阶段的工作可分别概括为新fiber树的构建和更新最终效果的应用。

    render阶段

    render阶段实际上是在内存中构建一棵新的fiber树(称为workInProgress树),构建过程是依照现有fiber树(current树)从root开始深度优先遍历再回溯到root的过程,这个过程中每个fiber节点都会经历两个阶段:beginWork和completeWork。组件的状态计算、diff的操作以及render函数的执行,发生在beginWork阶段,effect链表的收集、被跳过的优先级的收集,发生在completeWork阶段。构建workInProgress树的过程中会有一个workInProgress的指针记录下当前构建到哪个fiber节点,这是React更新任务可恢复的重要原因之一。

    如下面的动图,就是render阶段的简要过程:

    React原理解析第一篇:核心概念

    commit阶段

    在render阶段结束后,会进入commit阶段,该阶段不可中断,主要是去依据workInProgress树中有变化的那些节点(render阶段的completeWork过程收集到的effect链表),去完成DOM操作,将更新应用到页面上,除此之外,还会异步调度useEffect以及同步执行useLayoutEffect。

    这两个阶段都是独立的React任务,最后会进入Scheduler被调度。render阶段采取的调度优先级是依据本次更新的优先级来决定的,以便高优先级任务的介入可以打断低优先级任务的工作;commit阶段的调度优先级采用的是最高优先级,以保证commit阶段同步执行不可被打断。

    Scheduler 的作用

    Scheduler用来调度执行上面提到的React任务。

    何为调度?依据任务优先级来决定哪个任务先被执行。调度的目标是保证高优先级任务最先被执行。

    何为执行?Scheduler执行任务具备一个特点:即根据时间片去终止任务,并判断任务是否完成,若未完成则继续调用任务函数。它只是去做任务的中断和恢复,而任务是否已经完成则要依赖React告诉它。Scheduler和React相互配合的模式可以让React的任务执行具备异步可中断的特点。

    优先级机制

    为了区分任务的轻重缓急,React内部有一个从事件到调度的优先级机制。事件本身自带优先级属性,它导致的更新会基于事件的优先级计算出更新自己的优先级,更新会产生更新任务,更新任务的优先级由更新优先级计算而来,更新任务被调度,所以需要调度优先级去协调调度过程,调度优先级由更新任务优先级计算得出,就这样一步一步,React将优先级的概念贯穿整个更新的生命周期。

    React优先级相关的更多介绍请移步 React中的优先级。

    双缓冲机制

    双缓冲机制是React管理更新工作的一种手段,也是提升用户体验的重要机制。

    当React开始更新工作之后,会有两个fiber树,一个current树,是当前显示在页面上内容对应的fiber树。另一个是workInProgress树,它是依据current树深度优先遍历构建出来的新的fiber树,所有的更新最终都会体现在workInProgress树上。当更新未完成的时候,页面上始终展示current树对应的内容,当更新结束时(commit阶段的最后),页面内容对应的fiber树会由current树切换到workInProgress树,此时workInProgress树即成为新的current树。

    function commitRootImpl(root, renderPriorityLevel) {
        ...
    
        // finishedWork即为workInProgress树的根节点,
        // root.current指向它来完成树的切换
        root.current = finishedWork;
    
        ...
    }
    

    两棵树在进入commit阶段时候的关系如下图,最终commit阶段完成时,两棵树会进行切换。 React原理解析第一篇:核心概念

    在未更新完成时依旧展示旧内容,保持交互,当更新完成立即切换到新内容,这样可以做到新内容和旧内容无缝切换。

    总结

    本文基本概括了React大致的工作流程以及角色,本系列文章会以更新过程为主线,从render阶段开始,一直到commit阶段,讲解React工作的原理。除此之外,会对其他的重点内容进行大篇幅分析,如事件机制、Scheduler原理、重点Hooks以及context原理。

    本系列文章耗时较长,落笔撰写时,17版本还未发布,所以参照的源码版本为16.13.1、17.0.0-alpha.0以及17共三个版本,我曾经对文章中涉及到的三个版本的代码进行过核对,逻辑基本无差别,可放心阅读。


    起源地下载网 » React原理解析第一篇:核心概念

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元