react在调用this.setState()的过程中使用的脏处理(从root节点开始调用),在以前的版本中就会导致卡顿,尽管有虚拟dom,diff算法,但是如果递归的足够深,如div-div一直有100个div,在重新渲染阶段会造成卡顿现象,如在渲染过程中不能触发点击事件等,也正是因为这个原因,fiber架构的出现就是为了解决递归过深,会造成无可避免的卡顿这个问题。
fiber架构采用了chrome浏览器的一个api requestIdleCallback(callback[, options]),在浏览器的空闲阶段调用,这里可能会扯很多浏览器的概念,由于这个api目前只支持谷歌,所以react也对此方法进行了重写。
浏览器的空闲阶段调用究竟是怎么回事呢?就必须要说一说浏览器了。
浏览器是一个多进程。Browser 进程,第三方插件进程,gpu进程,和重中之重浏览器内核。
下面我们说一下浏览器内核,一个进程可以有多个线程。那浏览器内核也不为过
如gui渲染线程,js引擎线程,定时器线程,事件触发线程,http请求线程。
我们经常会说gui渲染线程和js引擎线程互斥,因为js在浏览器中运行也可以操作dom,为了防止渲染出现问题,所以互斥,就说到这里。
浏览器在每一帧(时间片)都是在工作的,和我们人一样,上班都需要工作,只是在工作的时候没有活,我们可以偷懒一会。
一个看上去不卡的浏览器,必须需要1s中完成60帧,也就是16ms为一帧,当你的显卡更好的时候,肯定是不止60帧这个数值。但是这里就使用60帧,这个概念,所以说打游戏的时候fps低帧数低,每秒加载的帧数低就算网络不卡,你也会玩起来很卡。
每一帧
1.首先会先进行事件处理,如果有事件触发了且有回调函数,如触发点击事件,且有回调函数,会把回调函数放到eventLoop的宏任务队列中。
2.然后执行js,在js调用栈为空,先去执行宏任务队列最先执行的,再执行所有微任务队列里面的方法,执行完再去执行js调用栈,来回循环调用。
3.执行requestanimationframe,再重绘之前执行
4.解析html --先把html解析成dom树,构建渲染树,布局渲染树,绘制渲染树,这也是一个经常会出的面试题,重绘 -重排(重绘-回流)。
5.剩余时间片,好比人工作总会有没事的时候。requsetIdleCallback就是再剩余时间片执行,如果这个帧没有剩余时间片,那就下一个帧寻找是否有剩余时间片。
我们渲染阶段放到requestIdleCallback中,之前的递归必须递归到底,但是现在我可以放到每一个时间片之中,requestIdleCallback(workLoop,{timeout:1000}),再workloop方法中我再调用requestIdleCallback(workLoop,{timeout:1000}),这样每一帧的空闲时间我都会去调用workloop方法,这样我就不需要一次性递归到底,也就避免了造成卡顿。
下面我来说一说fiber是怎么实现的。
这里说的肯定不可能太具体,只能讲一点思路,下次写博客深入理解fiber机构。
如果了解过一点fiber的数据结构知道,fiber的数据结构中有
React.createElement是为了帮我们创建虚拟dom的。我们需要通过虚拟dom去创建对应的fiber。
componentDidmounted是从外到内执行的,好比先序遍历,而componentWillUnmounted是从里往外执行,后序遍历。
那么fiber调用的时候肯定就会出现这两个阶段。
在先序遍历的时候我们调用beginWork 根据先给fiber.stateNode=dom/new实例,在给其子节点创建fiber
在后续遍历中,我们调用completeUnitOfWork,完成effectlist,
effectlist 先调用根节点的firstEffect即d,然后一次nexteffect,即d-b-c-a
最后在commit阶段根据effectList,依次appendChild,removeChild,或者更新属性,完成渲染。