介绍
有不少文章详细讲述了输入url到浏览器渲染出画面的过程,但很少有浏览器每一帧的具体渲染情况。因此本文将着重阐述以及两个api:requestIdleCallback、requestAnimationFrame
requestAnimationFrame
官方文档:developer.mozilla.org/zh-CN/docs/…
rAF是官方推荐的用来做一些流畅动画所应该使用的 API,做动画不可避免的会去更改 DOM,而如果在渲染之后再去更改 DOM,那就只能等到下一轮渲染机会的时候才能去绘制出来了,这显然是不合理的。
从下面的每帧流程也可以看出rAF是在渲染之前运行的。
为什么?setTimeout的问题
定时器的回调函数,会受到js的事件队列宏任务、微任务影响,可能设定的是17毫秒执行一次,但是实际上这次是17毫秒、下次21毫秒、再下次13毫秒执行,所以并不是严格的卡住了这个60HZ的时间。于是就出现了,绘制不及时的情况,就会有抖动的出现。
requestAnimationFrame能够做到,精准严格的卡住显示器刷新的时间,比如普通显示器60HZ它会自动对应17ms执行一次,比如高级显示器120HZ,它会自动对应9ms执行一次。
requestIdleCallback
rIC的意图是让我们把一些计算量较大但是又没那么紧急的任务放到空闲时间去执行。不要去影响浏览器中优先级较高的任务,比如动画绘制、用户输入等等。
React 的时间分片渲染就想要用到这个 API,不过目前浏览器支持的不给力,他们是自己去用 postMessage 实现了一套。
rIC提供deadline字段以提示开发者浏览器还有多少剩余时间,因此可以动态的分配任务。但正是由于完全信任开发者,因此也需要自行决策执行哪些任务以防止阻塞浏览器渲染。
需要注意的是,可能在几帧的时间内浏览器都是空闲的,并没有发生任何影响视图的操作,它也就不需要去绘制页面: 这种情况下为什么还是会有 50ms 的 deadline 呢?是因为浏览器为了提前应对一些可能会突发的用户交互操作,比如用户输入文字。如果给的时间太长了,你的任务把主线程卡住了,那么用户的交互就得不到回应了。50ms 可以确保用户在无感知的延迟下得到回应。
总结
-
事件循环不一定每轮都伴随着重渲染,但是如果有微任务,一定会伴随着微任务执行。
-
决定浏览器视图是否渲染的因素很多,浏览器是非常聪明的。
-
requestAnimationFrame在重新渲染屏幕之前执行,非常适合用来做动画。 -
requestIdleCallback在渲染屏幕之后执行,并且是否有空执行要看浏览器的调度,如果你一定要它在某个时间内执行,请使用timeout参数。