react fiber执行原理示例解析
为什么要使用fiber ,要解决什么问题?
在 react16 引入 Fiber 架构之前 ,react 会采用递归方法对比两颗虚拟DOM树,找出需要改动的节点 ,然后同步更新它们 ,这个过程 react 称为reconcilation(协调) 。在reconcilation期间 ,react 会同步执行操作 ,提交到真实 DOM 的更改 ,会一直占着浏览器的资源 ,不能中断 ,中断后就不能恢复 ,使得我们一些用户操作定时器等等事件无法得到响应 ,是一个非常糟糕的用户体验 。
所以我们要解决的问题就是:解决React主线程长时间占用的一个问题。 这个时候,就引入了Fiber架构 。
fiber是什么?
Fiber 可以理解为是一个执行单元 ,也可以理解为是一种数据结构 。每一个React元素都对应一个fiber对象 ,我们先看看fiber中的属性:
数据结构
React Fiber 就是采用链表实现的,主要就是通过以下这几个属性表示:
假如我们要渲染下面这个元素树:
我们看一下它的Fiber结构树:
每个fiber元素都有这三个属性 ,观察上面图发现:
parent:指向父级Fiber节点: child:指向子Fiber节点 sibling:指向右边的兄弟节点执行单元
我们可以把每个fiber当做一个执行单元 ,每次执行完一个执行单元 。React会去检测还剩多少时间 ,如果没有时间就将控制权让给浏览器 ,如果还有时间就去执行下一个执行单元 。
这里就涉及到了一个问题 ,react如何和浏览器进行控制权的交接 ,浏览器何时空闲呢? 。我们先来了解一下浏览器的工作:浏览器工作:
在浏览器中,我们所看到的页面是一帧一帧画出来的 ,渲染的帧率与设备的刷新率保持一致 。通常情况下 ,我们的设备都是60Hz,也就是说,1s屏幕会刷新60次 。当每秒内绘制的帧数(FPS)超过60时 ,页面渲染是流畅的 ,当帧数小于60时,会明显感受到卡顿。下面来看完整的一帧中 ,浏览器具体做了哪些事情:
首先需要处理输入事件 ,能够让用户得到最早的反馈 接下来是处理定时器 ,需要检查定时器是否到时间 ,并执行对应的回调 接下来处理 Begin Frame(开始帧) ,即每一帧的事件 ,包括 window.resize 、scroll 、media query change 等 接下来执行请求动画帧 requestAnimationFrame(rAF) ,即在每次绘制之前 ,会执行 rAF 回调 紧接着进行 Layout 操作 ,包括计算布局和更新布局,即这个元素的样式是怎样的 ,它应该在页面如何展示 接着进行 Paint 操作 ,得到树中每个节点的尺寸与位置等信息,浏览器针对每个元素进行内容填充 到这时以上的六个阶段都已经完成了 ,接下来处于空闲阶段(Idle Peroid) ,可以在这时执行 requestIdleCallback 里注册的任务这样我们把工作单元的任务放到requestIdleCallback回调当中,如果浏览器处理完上述的任务(布局和绘制之后) ,还有盈余时间 ,这个时候就可以执行我们的工作单元了 。每次执行完一个执行单元 。React会去检测还剩多少时间 ,如果没有时间就将控制权让给浏览器。直至 ,React和浏览器通过合作式调度完美配合 ,实现高性能应用 。
Fiber执行原理
从根节点开始调度和渲染可以分为两个阶段:render和commit 。 先来了解下这几个关键名词:
workInProgress tree:
workInProgress 代表当前正在执行更新的 Fiber 树。在 setState或者渲染 后 ,会构建一颗 Fiber 树 ,也就是 workInProgress tree ,
currentFiber tree:
首次渲染之后 ,React 会生成一个对应于 UI 渲染的 fiber 树,称之为 current 树 。在新一轮更新时 workInProgress tree 再重新构建 ,新workInProgress的节点通过 alternate 属性和 currentFiber 的节点建立联系 。
Effects list:
effect list 可以理解为是一个存储 effect 副作用列表容器 。
render阶段:
在render阶段中 ,会找到所有节点的变更,比如说节点新增 ,编辑 ,删除等等 。这些变更React称之为副作用effect 。在这个阶段中,也可以认为是diff阶段,主要就是对比currentFiber tree和workInProgress tree之间的差异 ,然后打上标记 。
在这个阶段 ,任务是可以终止的 。React 可以根据当前可用的时间片处理一个或多个 fiber 节点 ,并且得益于 fiber 对象中存储的元素上下文信息以及构成的链表结构 ,使其能够将执行到一半的工作仍保存在内存的链表中 。在重新获得控制权后 ,又可以根据保存在内存中的上下文信息快速找到停止的fiber节点 ,然后继续工作执行工作单元 。
遍历节点过程:遍历Fiber tree时采用的是后序遍历方法
从顶部开始遍历 如果有child节点 ,且还未遍历 ,遍历child节点 如果有child节点 ,且已经遍历过,则遍历sibling节点。 如果没有child节点 ,返回父节点 如果最后返回的节点为顶部 ,表示所有节点遍历完成 。 收集effect list:在遍历的过程中,我们会去收集所有变更的节点产出的effect,每个effect通过链表的方式链接 。每个 fiber 有两个属性
firstEffect:指向第一个有副作用的子fiber lastEffect:指向最后一个有副作用的子fiber中间的使用nextEffect做成一个单链表。
commit阶段:
与render阶段不同,commit阶段是同步操作的 。
为什么commit必须是同步的操作的?因为在commit阶段是更新真实的dom ,所以更新dom不可能一点一点去更新 ,这样用户体验会极差 。所以commit阶段必须是同步执行,一次更新到位。
首先的事情是遍历effect-list列表,拿到每一个 effect 存储的信息 ,根据副作用类型 effectTag 执行相应的处理并提交更新到真正的 DOM 。所有的effects都会在layout phase阶段之前被处理 。当该阶段执行结束时 ,workInProgress树会被替换成current树 。到这里 ,根据收集到的变更信息完成了刷新操作 。
以上就是react fiber执行原理示例解析的详细内容 ,更多关于react fiber执行原理的资料请关注本站其它相关文章!
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!