React 为在有限的资源情况下,更好地控制UI的更新,提出了时间分片的概念。以达到三个目标:

  performing non-blocking rendering(无阻塞渲染);applying updates based on the priority(根据优先级渲染);pre-rendering content in the background(预渲染)。

  React 遍历Dom Tree:

  1. updates state and props(更新状态和属性)

  2. calls lifecycle hooks(执行生命周期回调函数)

  3. retrieves the children from the component(找到子组件)

  4. compares them to the previous children (对子组件进行diff)

  5. figures out the dom updates the need to be performed(找出变更,执行dom操作)

  when dealing with UIs, the problem is that if too much work is executed all at once ,it can cause animations to drap frames。(当在有太多内容要更新时,会出现掉帧的现象,即卡顿,更新跟不上浏览器的刷新速度)

  if react is going to walk the entire tree of components synchromously and perform work for each component ,it may run over 16ms available for an application code to execute its logic 。And this will cause frames to drop causing stuttering visual effects.

  在理解react如何通过时间分片的来实现对用户而言的无阻塞渲染之前,需要了解两个浏览器的api:

  requestIdleCallback会在某一帧结束后的空余时间或者用户处于不活跃的的状态时,处理我们的工作。利用这个API能使我们尽可能高效利用任何空闲的资源。

  requestAnimationFrame 是浏览器用于定时循环操作的一个接口,类似于setTimeout,主要用途是按帧对网页进行重绘。设置这个API的目的是为了让各种网页动画(

Dom动画,Canvas动画,SVG动画,WebGL动画)能够有一个统一的刷新机制。显示器固有的刷新频率(60HZ/75HZ),requestAnimationFrame的基本思想就是与这个刷新频率保持一致。

 requestIdleCallback((deadline)=>{

       // 可用时间 & 是否有可用时间
     console.log(deadline.timeRemaining(),deadline.didTimeOut)     })  //timieRemaining can change as soon as browser gets some work to do,so it should be constanly checked。

  要知道浏览器渲染一帧后的剩余时间,除了浏览器本身,利用js基本很难准确计算得到,因为当requestAnimationFrame的回调完成后,还要进行样式的计算,布局,渲染以及浏览器内部的工作等,还要确保当前没有用户交互。

  当事件很多时,你可能会担心你的回调函数永远不会被执行。requestIdleCallback有一个可选的第二参数:含有timeout属性的对象。如果设置了这个timeout的值,回调函数还没有调用的话,则浏览器必须在设置的这个毫秒数后,去强制调用对应的回调函数。如果回调函数由timeout触发,timeRemaining()会返回0,deadtime对象的didTimeout属性值true。(设置timeout会破坏潜规则)

  如果在某一帧的末尾,回调函数被触发,它将被安排在当前帧被Commit之后,这表示相应的样式已经改动,同时更重要的是布局已经重新计算。如果我们在这个回调中进行样式的改动,设计到布局的计算则会被判无效。如果在下一帧中有任何读取布局相关的操作,浏览器不得不执行一次强制同步布局,这将是一个潜在的性能瓶颈。此外不要在回调函数中触发dom改动的原因是DOM改动的时间是不可预期的,正因为如此,DOM操作很容易地超过浏览器给出的限期。

  最佳实践是在requestAnimationFrame的回调中去进行dom改动,因为浏览器会优化同类型的改动。这意味可以在下一次的requestAnimationFrame回调中添加一个文档片段。如果用的是虚拟DOM的库,你可在requestIdleCallback中做改动,但要在下一次requestAnimationFrame中将这些改动应用到Dom上,而不是在requestIdleCallback中。

  为了使用这些API,需要把整个tree的渲染工作划分为可逐渐递增的单元。同时为了达到这个目标,React需要重新实现遍历树的算法,从之前的依赖内部调用栈的同步递归模型转向通过指针链接链表的异步模型。原因在于如果依赖内部调用栈,遍历更新的工作会一直进行直到调用栈为空。React Filter的目标在于把一次连续的树的遍历操作变为可单步执行的栈的片段,以达到无阻塞渲染和优先级渲染的目的。

  (调用堆栈是一种数据结构,用来存储有关计算机程序活跃子程序的信息,调用堆栈存在的主要原因是跟踪每个活跃的子程序在完成执行时应该返回的控制位置。)

  递归的算法非常直观,适合进行树的遍历操作,但是它是有局限性的,最大的一点就是无法分解工作为增量单元,这使得React不能暂停特定组件的工作并在稍后恢复。通过递归,React只能不断迭代直到它处理完成所有组件,堆栈为空。

  React Fiber采用邻接链表树遍历算法来代替递归遍历。

// 结构体
class Node{
constructor(instance){
this.instance = instance;
this.child = null; // fisrt child
this.sibling = null; // first sibling
this.return = null; // parent
}
} function link(parent,elements){
if(elements === null) elements =[];
parent.child = elements.reduceRight((previous,current)=>{
const node = new Node(current);
node.return = parent;
node.sibling = previous;
return node;
},null)
return parent.child
}
// the function iterates over the array of nodes starting from the last one and links them //together in a singly linked list. It returns the reference
// to the first sibling in the list
function doWork(node){
  const children = node.instance.render()
  return link(node,children)
}
//it's parent first,depth-first implementation
function walk(o){
let root = o;
let current = o;
while(true){
let child = doWork(current);
    if(child){
     current = child
     continue;
    } 
    if(current === root){
     return ;
    }
    while(!current.sibling){
      if(!current.return || current.return === root){
         return;
      }
      current = current.return;
    } 
}
}
// Fiber is re-implementation of the stack,specialized for React components.You can think of a //single fiber as a virtual stackframe
// we can stop the traversal at any time and resume to it later.That's exactly the condition we //wanted to achieve to be able to use the new
// requestIdleCallback API
  function workLoop(isYieldy){
    if(!isYieldy){
      while(nextUnitOfWork !== null){
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
      }
    }else{
      while(nextUnitOfWork !== null && !shouldYiels()){
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
      }
    }
  }
// nextUnitOfWork 变量作为顶部帧,保留对当前Fiber节点的引用。函数shouldYield返回基于//deadlineDidExpire和deadline变量的结果,这些变量在React为Fiber节点执行
// 工作时不停的更新

  react更新UI,要做的所有操作都是Fiber的要做工作。work的类型依据元素的类型。在协调期间,每个从render 函数返回的react节点,会被合并到fiber节点树中。每一个react节点都有对应的fiber节点,不同的是,fiber并不会在每次重新render之后,重新创建。fiber节点其实是可变的数据结构保存了组件的状态的dom结构。每个fiber可以认为代表了不同类型的工作要做。这使得fiber可以根据优先级进行渲染。

  

  

  

 

React 使用链表遍历组件树的更多相关文章

  1. React之使用Context跨组件树传递数据

    ---------------------------------  讲解一 原文:https://blog.csdn.net/xuxiaoping1989/article/details/78480 ...

  2. React.js 小书 Lesson8 - 组件的组合、嵌套和组件树

    作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson8 转载请注明出处,保留原文链接和作者信息. 继续拓展前面的例子,现在我们已经有了 Heade ...

  3. 前端笔记之React(二)组件内部State&React实战&表单元素的受控

    一.组件内部的State 1.1 state state叫状态,是每一个类式组件都有的属性,但函数式组件,没有state. state是一个对象,什么值都可以定义. 在任何类式组件的构造函数中,可以用 ...

  4. React和Vue的组件更新比较

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px "Helvetica Neue"; color: #404040 } p. ...

  5. 从 Vue 的视角学 React(四)—— 组件传参

    组件化开发的时候,参数传递是非常关键的环节 哪些参数放在组件内部管理,哪些参数由父组件传入,哪些状态需要反馈给父组件,都需要在设计组件的时候想清楚 但实现这些交互的基础,是明白组件之间参数传递的方式, ...

  6. react基本语法及组件

    一.react简介 1.起源:React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源. 2.特点: 1.声明式设计 −React采用声明 ...

  7. 玩转 React(五)- 组件的内部状态和生命周期

    文章标题总算是可以正常一点了-- 通过之前的文章我们已经知道:在 React 体系中所谓的 "在 JavaScript 中编写 HTML 代码" 指的是 React 扩展了 Jav ...

  8. react实战系列 —— react 的第一个组件

    react 的第一个组件 写了 react 有一个半月,现在又有半个月没写了,感觉对其仍旧比较陌生. 本文分两部分,首先聊一下 react 的相关概念,然后不使用任何语法糖(包括 jsx)或可能隐藏底 ...

  9. react native之组织组件

    这些组件包括<TabView>,<NavigatorView>和<ListView>,他们实现了手机端最常用的交互和导航.你会发现这些组件在实际的项目中会非常有用. ...

  10. POJ 3321 Apple Tree(后根遍历将树转化成序列,用树状数组维护)

    题意:一棵树,有很多分叉,每个分叉上最多有1个苹果. 给出n,接下来n-1行,每行u,v,表示分叉u,v之间有树枝相连.这里数据中u相当于树中的父节点,v相当于子节点. 给出两个操作: 1.C x  ...

随机推荐

  1. 【OpenWRT】增加第三方开源库 - 二维码开源库 zbar

    序言 第一次开始写博客,在日常学习和工作当中 CSDN 给我帮助很大,因此我也在 CSDN 奉献自己的经验,借此回馈 CSDN 对我的帮助,希望自己的经验可以帮助需要的人,也方便自己后续复习之用,同时 ...

  2. spring.jackson.default-property-inclusion 不生效问题分析

    背景 项目里每个返回体里都有@JsonInclude(JsonInclude.Include.NON_NULL) 这个注解,也就是不返回null字段 想有没有办法全局配置一下,这样就不用每个类都加这个 ...

  3. python实现通用excel导入到mysql

    { "file": "OrderDetail-2020-06-03.xls", "startRow": 1, "table&quo ...

  4. 实现一个简单的在浏览器运行Dotnet编辑器

    之前已经实现过Blazor在线编译了,现在我们 实现一个简单的在浏览器运行的编辑器,并且让他可以编译我们的C#代码, 技术栈: Roslyn 用于编译c#代码 [monaco](microsoft/m ...

  5. Git-01 简要介绍

    1 git简介 Git 是一个免费的.开源的分布式版本控制系统,可以快速高效地处理从小型到大型的各种项目. Git 易于学习,占地面积小,性能极快. 它具有廉价的本地库,方便的暂存区域和多个工作流分支 ...

  6. Springboot返回数据给前端-参数为null处理

    转:https://www.pianshen.com/article/950119559/ 1.返回对象参数为null时,该参数选择显示或者不显示 在返回参数给前端的时候,有些参数的值为null的时候 ...

  7. springcloud 08 Hystrix图形化DashBoard

    #1.构建一个服务监控模块 ##1.1创建模块cloud-consumer-hystrix-dashboard9001 ##1.2pom文件依赖 <dependencies> <!- ...

  8. 多功能游戏工具箱 - Watt Toolkit V4.4

    Watt Toolkit Watt Toolkit 工具箱能够让 Steam 平台的玩家们享受更加出色的游戏体验,工具箱包含多种实用的功能,支持快速切换登录账号,玩家还可以通过这款工具编辑 Steam ...

  9. Autodesk Maya2023 破解版安装教程(小白看了也说understand)

    前言 Maya是Autodesk旗下的著名三维建模和动画软件,应用对象是专业的影视广告,角色动画,电影特技等.Maya功能完善,工作灵活,制作效率极高,渲染真实感极强,是电影级别的高端制作软件. 安装 ...

  10. Python接口自动化测试(1)

    接口自动化测试三部曲:1.构造请求  2.判断结果  3.数据库查询 1.Python的第三方包:requests 简介:requests可以用来做接口测试.接口自动化测试.爬虫等 requests的 ...