React 的简略发展史

664 阅读4分钟

个人的粗浅理解,简单整理记录

React 诞生的由来

主要是当时市面上的这些框架都无法满足 facebook 公司的业务需求。认为MVC不适合大规模的应用。当系统中有很多模型和相应的视图时,其复杂度就会迅速扩大,非常难以理解和调试,特别是模型和视图可能存在双向数据流动。

解决这个问题需要“以某种方式组织代码,使其更加可预测”,这通过Flux和React已经完成

Flux是一个系统架构,用于推进应用中的数据单向流动。React是一个JavaScript框架,用于构建“可预期的”和声明式的”Web用户界面”,它已经使Facebook更快地开发Web应用。

PureComponent的诞生

React component 内部通过调用setState(),来触发组件的render,如果我们内部包含有子组件,但是子组件需要的props并没有改变,也会因为setState()而进行不必要的render,所以大家会普遍性的在组件内增加shouldComponentUpdate,来判断是否需要render,减少不需要的render,从而提高性能。后来,pureComponent诞生了,内置封装了一层shouldComponentUpdate,但是仅仅对进来的props进行了一次浅比较shallowEqual

Immutable库的配合使用

由于pureComponent内部只是shallowEqual,我们的props又经常是深层对象,所以在改变对象的深层属性值时,会发现组件得不到正确的render,pureComponent会认为它不需要render。因为Immutable库会引入进来,和pureComponent一起工作,达到正确判断是否需要render。详细内容可移步至我的另一片记录: 重新理解PureComponent + Immutable 的联合使用

Fiber的诞生

在早期的 React 版本中,也就是 React16.8 版本之前。

大量的同步计算任务阻塞了浏览器的UI渲染。默认情况下,JS运算页面布局页面绘制渲染都是运行在浏览器的主线程当中,他们之间是互斥的关系。

如果 JS 运算持续占用主线程,页面就没法得到及时的更新,当我们调用setState更新页面的时候,React 会遍历应用的所有节点,与老的 vdom 节点进行 diff 算法的对比,最小代价更新页面,即使这样,整个过程也是一气呵成,不能被打断的,如果页面元素很多,整个过程占用的时间就可能超过16毫秒,出现掉帧的现象。

针对这一现象,React 团队从框架层面对 web 页面的运行机制做了优化,此后,Fiber诞生了。

Fiber实现了自己的组件调用栈,它以链表的形式遍历组件树,可以灵活地暂停、继续和丢弃执行的任务。实现的方式是使用了 浏览器的requestIdleCallback这一 API。官方的解释是这样的

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

总的来说:Fiber是将react的架构从最初的树形结构改为了链表结构,因为链表结构时可以暂停/恢复的,借此结构来在浏览器空闲时间来进行我们的vdom diff算法,当浏览器繁忙需要进行渲染的时候,交还控制权给浏览器,等下次空闲时间继续刚才没干完的diff任务。

说到16ms,我们来看这样的一个概念

屏幕刷新率

  • 目前大多数设备的屏幕刷新率为60次/秒
  • 浏览器的渲染动画或页面的每一帧的速率也需要跟设备屏幕的刷新率保持一致。
  • 页面是一帧一帧绘制出来的,当每秒绘制的帧数(FPS)达到60时,页面是流畅的,小于这个值时,用户会感觉到卡顿。
  • 每个帧的预算时间是16.66毫秒(1秒/60)
  • 1s 60帧,所以我们书写代码时尽量不让一帧的工作量超过16ms

hook诞生的由来

为什么需要hooks 或者说 hooks解决了什么问题?

  • 在组件之间复用状态逻辑难
    • 自定义hook可以轻松实现state的复用状态。
  • 复杂组件变得难以理解
    • 高阶组件
  • 难以理解的class
    • this的工作机制
    • 语法

详细内容推荐阅读: React为什么需要Hook