此文为对 https://juejin.cn/post/6844903975112671239#heading-5 一文的总结
1. 计算机操作系统最开始的调度策略
'一种压根没有任务调度的“残疾”操作系统'.
在这种系统中,你想执行多个任务,只能等待前一个进程退出,然后再载入一个新的进程。
2. 后来陆续出来了一些什么样的调度策略
- 先到先得
- 轮转
- 最短进程优先
- 最短剩余时间
- 最高响应比优先
- 反馈法
3. 对于’前端框架‘来说,如何提升CPU计算的使用效率:
- 优化每个任务,让它有多快就多快。挤压CPU运算量(
vue
:响应式机制可以精确地进行节点更新) - 快速响应用户,让用户觉得够快,不能阻塞用户的交互(
react
: fiber) - 尝试 Worker 多线程
3.fiber本质上解决的是一个什么问题:
react渲染页面分为两个阶段:diff算法阶段和commit提交渲染阶段,是关于多任务(交互响应,以及css加载解析,js加载解析,渲染树布局绘制等等)调度策略的一个问题
4. 那以前的调度策略是怎样的
以前是diff算法和commit提交渲染过程均为同步,一气呵成,造成如果页面过大用时也会过久,没有给用户交互响应的空间,从而显得卡顿。
5. 现在的调度策略是怎样的
为了给用户制造一种应用很快的'假象',我们不能让一个程序长期霸占着资源. 你可以将浏览器的渲染、布局、绘制、资源加载(例如HTML解析)、事件响应、脚本执行视作操作系统的'进程',我们需要通过某些调度策略合理地分配CPU资源,从而提高浏览器的用户响应速率, 同时兼顾任务执行效率。
🔴所以 React 通过Fiber 架构,让自己的Reconcilation(diff) 过程变成可被中断。 '适时'地让出CPU执行权,除了可以让浏览器及时地响应用户的交互,还有其他好处:
6. 那具体是怎么实现的
- 通过requestIdleCallback 实现在浏览器渲染帧间隙之间执行;
但是在浏览器繁忙的时候,可能不会有盈余时间,这时候requestIdleCallback回调可能就不会被执行。 为了避免饿死,可以通过requestIdleCallback的第二个参数指定一个超时时间。 - 这个超时时间不是死的,低优先级的可以慢慢等待, 高优先级的任务应该率先被执行。目前 React 预定义了 5 个优先级
- Immediate(-1) - 这个优先级的任务会同步执行, 或者说要马上执行且不能中断
- UserBlocking(250ms) 这些任务一般是用户交互的结果, 需要即时得到反馈
- Normal (5s) 应对哪些不需要立即感受到的任务,例如网络请求
- Low (10s) 这些任务可以放后,但是最终应该得到执行. 例如分析通知
- Idle (没有超时时间) 一些没有必要做的任务 (e.g. 比如隐藏的内容), 可能会被饿死
- 通过超时检查的机制来让出控制权:确定一个合理的运行时长,然后在合适的检查点检测是否超时(比如每执行一个小任务),如果超时就停止执行,将控制权交换给浏览器。
- 目前
requestIdleCallback
目前只有Chrome支持。所以目前 React 自己实现了一个。它利用MessageChannel
模拟将回调延迟到'绘制操作'之后执行:
7. React 的Fiber改造
1. 数据结构的调整:使用链表
改造前:递归调用栈:栈挺好的,代码量少,递归容易理解, 至少比现在的 React Fiber架构好理解😂, 递归非常适合树这种嵌套数据结构的处理。只不过这种依赖于调用栈的方式不能随意中断、也很难被恢复, 不利于异步处理。 这种调用栈,不是程序所能控制的, 如果你要恢复递归现场,可能需要从头开始, 恢复到之前的调用栈。
改造后:模拟函数调用栈, 将之前需要递归进行处理的事情分解成增量的执行单元,将递归转换成迭代**.
2. 两个阶段的拆分
Reconciliation(diff)阶段:因为协调阶段可能被中断、恢复,甚至重做,⚠️React 协调阶段的生命周期钩子可能会被调用多次!, 例如 componentWillMount 可能会被调用两次。索性 React17 就废弃了这部分可能包含副作用的生命周期方法,例如componentWillMount、componentWillUpdate
commit提交阶段:此阶段要正确地处理各种副作用,包括DOM变更、还有你在componentDidMount中发起的异步请求、useEffect 中定义的副作用... 因为有副作用,所以必须保证按照次序只调用一次,况且会有用户可以察觉到的变更, 不容差池。
3. 双缓冲
WIP 树构建这种技术类似于图形化领域的'双缓存(Double Buffering)'技术, 图形绘制引擎一般会使用双缓冲技术,先将图片绘制到一个缓冲区,再一次性传递给屏幕进行显示,这样可以防止屏幕抖动,优化渲染性能。
放到React 中,WIP树就是一个缓冲,它在Reconciliation 完毕后一次性提交给浏览器进行渲染。它可以减少内存分配和垃圾回收
,WIP 的节点不完全是新的,比如某颗子树不需要变动,React会克隆复用旧树
中的子树。
双缓存技术还有另外一个重要的场景就是异常的处理
,比如当一个节点抛出异常,仍然可以继续沿用旧树的节点,避免整棵树挂掉。
4. 副作用的收集和提交
此处没明白
网友评论