浏览器渲染与 ReactFiber 关系

公众号【码农爱摸鱼】,专注于在摸鱼中愉快的工作和学习~

我们知道 JS 是单线程的,同一时间只能执行一件事情。

而在我们前端使用框架书写页面代码时,就算是大数据量的渲染和 dom 操作,也不会在浏览器上体会出很强的卡顿。

这得力于在我们使用的框架中,均在处理数据和浏览器渲染这俩个事件上做过代码处理已达到减轻或者去除可能会导致浏览器渲染卡顿的情况。

而在 React 中,于 16 版本之后对此渲染处理的模块单独做过优化行成了新的调度策略(Fiber),今天我们就来浅浅的了解一下。

浏览器渲染

首先,咱来了解一下,在浏览器中页面是一帧一帧绘制出来的,当每秒绘制的帧数(FPS)达到 60 时,页面是流畅的,小于这个值时,用户会感觉到卡顿。 1s 60 帧,所以每一帧分到的时间是 1000/60 ≈ 16 ms。所以我们书写代码时力求不让一帧的工作量超过 16ms。

在一帧中浏览器所需要完成的事情

通过图可看到,一帧内需要完成如下六个步骤的任务:

  • 1.处理用户的交互
  • 2.JS 解析执行
  • 3.帧开始。窗口尺寸变更,页面滚去等的处理
  • 4.requestAnimationFrame(rAF)
  • 5.布局
  • 6.绘制

了解了上面的生命周期之后,我们再来看 2 个函数:

requestIdleCallback

上面六个步骤完成后没超过 16 ms,说明时间有富余,此时就会执行requestIdleCallback里注册的任务。 下面是一个简单的示例:

scss 复制代码
requestIdelCallback(task);

function task (deadline) {
  // deadline.timeRemaining()可以获取到当前帧剩余时间
  while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    doWorkIfNeeded();
  }
  if (tasks.length > 0){
    requestIdleCallback(task);
  }
}

requestAnimationFrame

requestAnimationFrame方法就是告诉浏览器你希望执行一个动画。

并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。

该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

上面这句话引自 MDN

说通俗点就是,该 API 能以浏览器的显示频率来作为其动画动作的频率,例如某一设备的刷新率是 75 Hz,那这时的时间间隔就是 13.3 ms(1 秒 / 75 次),动画回调也每 13.3ms 调用一次;

Fiber

Fiber 架构的功能就是来处理这类渲染问题,其主要思想如下:

  1. 解决主线程长时间被 JS 运算占用这一问题的基本思路,是将运算切割为多个步骤,分批完成。也就是说在完成一部分任务之后,将控制权交回给浏览器,让浏览器有时间进行页面的渲染。等浏览器忙完之后,再继续之前未完成的任务。

  2. React 将任务分成小片,在一小片段的时间内运行这些分片任务,让主线程做优先级更高的事情,如果有任何待处理的事情,就回来完成工作。一个 Fiber 就是一个工作单元, React 的一个核心概念是 UI 是数据的投影 ,组件的本质可以看作输入数据,输出 UI 的描述信息(虚拟 DOM 树),即:

    ui = f(data)

    也就是说,渲染一个 React app,其实是在调用一个函数,函数本身会调用其它函数,形成调用栈,递归调用导致的调用栈我们本身无法控制, 只能一次执行完成。而 Fiber 就是为了解决这个痛点,可以去按需要打断调用栈,手动控制 stack frame------就这点来说,Fiber 可以理解为 virtual stack frame。

  3. 旧版 React 通过递归的方式进行渲染,使用的是 JS 引擎自身的函数调用栈,它会一直执行到栈空为止。而 Fiber 实现了自己的组件调用栈,它以链表的形式遍历组件树,可以灵活的暂停、继续和丢弃执行的任务。实现方式是使用了浏览器的 requestIdleCallback 这一 API。

    而对此,官方的解释是这样的:

window.requestIdleCallback()会在浏览器空闲时期依次调用函数,这就可以让开发者在主事件循环中执行后台和低优先级的任务,而且不会对像动画和输入响应等用户交互这些延迟触发但关键的事件产生影响。函数一般会按先进先调用的顺序执行,除非函数在浏览器调用它之前就到了它的超时时间。

而基于这些思想,最终完成了 Fiber 的优化,至于 Fiber 的核心内容和代码,咱后面再一起继续研究 🧐

我是摸鱼君,你的【三连】就是摸鱼君创作的最大动力,如果本篇文章有任何错误和建议,欢迎大家留言!

文章持续更新,可以微信搜索 【码农爱摸鱼】关注公众号第一时间阅读。

相关推荐
熊的猫27 分钟前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
瑶琴AI前端1 小时前
uniapp组件实现省市区三级联动选择
java·前端·uni-app
会发光的猪。1 小时前
如何在vscode中安装git详细新手教程
前端·ide·git·vscode
别拿曾经看以后~2 小时前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
我要洋人死2 小时前
导航栏及下拉菜单的实现
前端·css·css3
川石课堂软件测试2 小时前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
科技探秘人2 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人2 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR2 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香2 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel