大家好,我是右子。
React fiber利用了有两个核心的api。
requestAnimationFramerequestIdleCallback
是什么
fiber的出现是为了提升性能,解决卡顿、掉帧的现象。
怎么做
requestIdleCallback会在浏览器线程的任务空闲时执行回调函数,但是由于requestIdleCallback本身对浏览器的支持不是很好,所以react团队对它进行了模拟。
requestIdleCallback是一个宏任务
let activeFrameTime = 1000/60; // 设置每秒执行60帧
let passedFrameTime; // 记录是否超时
let frameCallback; // 回调
let channel = new MessageChannel();
channel.port2.onmessage = function(e){
let pft = e.data;
// 获得一个网页从加载起始时(navigationStart)到现阶段的时间
let pn = preformance.now();
// 拥有剩余的时间片
if(pft <= pn || pft - pn > 0){
frameCallback && frameCallback({
idleTime: (pft <= pn),
timeRemaining(){
return pft - preformance.now();
}
});
}
}
window.requestIdleCallback = function(cb,opts){
window.requestAnimationFrame(function(timeStamp){
// timeStamp: 它表示 requestAnimationFrame() 开始去执行回调函数的时刻
// 用于计算出是否有空闲时间
passedFrameTime = timeStamp + activeFrameTime;
// 下一帧回之前执行这个回调函数,赋值给外部变量。
frameCallback = cb;
channel.port1.postMessage(passedFrameTime);
});
};
为什么做这些呢?
屏幕刷新率(FPS)
- 浏览器的一般帧数是60/s,平均每帧16.6ms绘制一次。
- 有统计0-16ms的任务,用户会感觉非常流畅,0-100ms的任务,用户感觉基本流畅。
帧的规则
- 浏览器每帧的里都包括样式计算、布局和绘制
- 因为JS的执行是单线程,JS引擎和页面渲染引擎都占用同一个主线程,GUI的渲染和Javascript执行两者是互斥的
- 如果出现执行任务时间过长(Long Task),浏览器会延迟渲染,造成页面卡顿
- requestAnimationFrame 会在下一帧绘制之前调用回调函数。
- 再之后,如果还有剩余时间,会执行 requestIdleCallback。
为什么
fiber的渲染有两个阶段,协调阶段和提阶段。
- 协调阶段是遍历虚拟dom,进行diff深度优先遍历,收集差异的阶段,这个阶段可以被终止。
- 提交阶段是修改真实dom,进行页面绘制的阶段,操作时同步的,不能被打断。