深刻react源码--时间分片1(time-split)

时间分片

  早在react16发布以前,就有个新鲜玩意儿被炒的沸沸扬扬,对,就是时间分片这个东西。可是这个挺起来高大上的玩意儿究竟是个啥呢?接下来咱们就分析一下~
  在分析时间分片以前,咱们要先抛出三个问题:
                        1. 为何须要时间分片?
                        2. 时间分片是怎么实现的?
                        3. 所谓的时间分片它"分"的究竟是啥?
   接下来咱们就能够带着以上这三个问题一块儿去研究研究时间分片这个妮子了~

css

1. 为何须要时间分片?

  答: 由于牛逼!
  其实每一个新功能的诞生,都必定是为了解决某种可能会存在问题的应用场景的,不然就是个憨逼。因此时间分片也必然是为了处理react15中所存在的某种问题的。你们能够本身试验一下,使用react15去渲染1个动画 + 1万个节点,而后去更新这10万个节点,看看你的浏览器能不能顶得住,我这里准备了个demo,你们能够放进浏览器跑一跑~react15 demo
  你们能够发现,一旦当节点多了的时候,程序会变的比较卡顿,这样是react15以前一直被人病诟的缘由。因而这个时候,time split闪亮登场!
  那么问题来了,当页面中节点多了,使用时间分片就能够阻止卡顿吗?
  答案其实否认的。使用时间分片并不能阻止页面产生卡顿,与其说是阻止卡顿,更确切地说法实际上是: “减轻程序中,优先级较高的事物(好比css动画)的卡顿时间”。这句话可能目前不太好理解,等我们往下分析,一会就明了了,你们就先记住,时间分片的做用简单来说就是上面这句话~

html

2. 时间分片是怎么实现的?

  在说时间分片以前,咱们须要来了解一下浏览器中的“帧”的概念。
  首先“帧”是什么呢?咱们在浏览网页滑动滑轮页面会往下出溜对吧,或者css3出来以后常常能在网站中看到一些很炫酷的动画哈,其实咱们所看到的页面,从始至终都是静态的,可是之因此能看到页面很流畅的往下滑或者感到动画运行的很流畅的根本缘由,实际上是浏览器在不停地刷新页面,浏览器经过很高的频率去刷新页面让页面不停地变化,这才使咱们能感到页面的运行是流畅的。经验代表,当1秒钟页面刷新33次左右可以使咱们看到比较流畅的动画,也就是须要每隔 大约(1000ms / 33)33ms刷新一次页面。因此有时咱们使用setInterval写动画,第二个参数若是大于33ms可能会感受到不太流畅。很早之前你们据说过的flash动画,若是我没记错好像是一秒20帧吧,因此flash的体验并非特别好,如今也就被逐渐淘汰了。
  咱们如今知道了浏览器会不停地刷新页面(谷歌控制台里能够唤出显示刷新频率的小窗口),而且通常状况下每隔33ms刷新一下能够保证流畅(并不绝对是33ms,像谷歌有时候多是16ms刷新一次,vr好像是1秒刷新90次,3d好像是120次,咱们在这篇文章的后面都假设浏览器的刷新频率是 33ms),在这33ms内的页面实际上是静态不动的。
  那每一帧当中浏览器都干了什么事情呢?咱们来看下面一张图:react

  简单总结一下上面的图,其实浏览器的每一帧均可以概括为四个部分:
    执行js + 执行requestAnimation + 浏览器计算dom位置并从新刷新页面 + 第四部分
  上图中对于这“这第四部分”并无表示出来。其实还有另一张图:

  上图中黄色的部分其实就是所谓的“第四部分”。你们能够看到,这个部分执行了一个叫作idleCallback的东西,这是个啥呢?其实它也是浏览器原生提供的一个函数,它的全名叫作requestIdleCallback,和requestAnimationFrame很相似,是一个跟“帧”相关的回调函数。从第一张图能够看出requestAnimationFrame是执行在页面从新渲染以前的,而从第二张图片能发现,这个requestIdleCallback是执行在frame commit以后,在整个帧的最后,当咱们的业务代码的js,requestAnimationFrame,以及页面都从新渲染完了后,它才会执行。Idle的中文其实就是“闲散”的意思。换句话说, requestIdleCallback执行在每一帧(假设33ms)剩余的空闲时间中。
  啥意思呢,举个🌰,假设咱们的业务中的同步js代码执行了10ms,而后requestAnimationFrame中又修改了某个dom的位置,这部分的代码又执行了5ms,以后进入到layout步骤,浏览器底层开始从新计算dom们的位置,计算完了从新刷新了一边页面花费了5ms,最终:
  10ms + 5ms + 5ms = 20ms
  而向咱们刚刚所说的,每一帧是33ms,那么 33ms - 20ms = 13ms,这剩下的13ms干啥呢?没错!就干requestIdleCallback这个函数!这个函数中的全部代码都会放到这一帧所剩下的时间中去执行!
  咱们能够想想,把代码放在这一帧剩余的时间中有什么好处呢?
  咱们刚刚已经说过了,这个函数是执行在页面从新渲染以后,也就是说, 这个时间段执行的任何代码,不会影响到咱们视觉上的感知!
  再举个🌰:
    1. 我有个div,每次往右移动 1px,我想让他最终到达3px的位置
    2. 在requestAnimationFrame中每次让该div的 left 加 1px
    3. 第一帧的时候在 33ms 以内,第一步执行了js代码,第二步执行了requestAnimation,第三步刷新了页面,此时的 该div位置是 1px
    4. 第二帧的时候在 33ms 以内,第一步执行了js代码,第二步执行了requestAnimation,第三步刷新了页面,此时的 该div位置是 2px
    5. 第三帧的时候在 33ms 以内,第一步执行了js代码,第二步执行了requestAnimation,第三步刷新了页面,此时的 该div位置是 3px,达到了目的地
    6. 你们要注意的是,因为每次从新渲染页面,都发生在33ms以内,因此对于咱们的肉眼来说,第2步到第4步的div位移过程是一个连续运动的过程!
    7. 因此,若是这三帧,每一帧在刷新完页面以后还剩余一些时间的话,咱们在这部分时间中执行的代码,是彻底不影响咱们视觉上的流畅的!
  这个时候你们也许就能想到一个很牛逼的事情: “ 若是咱们把项目中的全部复杂的计算过程都放到这种一帧中剩余的时间中,那不就彻底不影响其余动画或者交互了嘛!
  没错!理论上讲确实是这样的!react16中,也正是应用这种方法,来作到尽可能减小对动画等优先级较高的操做的卡顿的。那么react16中是怎么作的呢?
   react16中,若是开启了异步渲染模式(时间分片模式)的话,就会把全部的复杂的操做放到每一帧的空闲时间中,从而达到不影响优先级较高的操做的目的。复杂的操做好比“diff算法”,“初始化函数或class组件”,“执行组件生命周期”,“建立或更新fiber树”等都算是复杂的操做。经过将这种复杂的同步算法,放到了不影响视觉流畅的空闲时间当中,这就是react16中的时间分片的假面!
  到此为止我们讨论了最开始提出的前俩个问题:为何要时间分片以及时间分片实现原理,可是为啥说是时间分片的“假面”呢?还有我们开始提出的第三个问题:时间分片分的究竟是啥呢,咱们下一回再讨论~

下一篇: 时间分片分的究竟是啥?
css3

相关文章
相关标签/搜索