这是我参与「第五届青训营」伴学笔记创作活动的第 6 天,欢迎各位大佬批评指正。
React 概述
React是目前最出色的前端框架之一,包括字节、阿里、腾讯、美团等多家大厂都在使用,许多网页也是基于React开发,比如:Facebook、Instagram、Netflix网页等。
React其实相对于传统的直接操作DOM编程的优势就在于将数据变动导致页面DOM更新的逻辑封装在框架内部,避免手动直接更改大量的页面DOM,大大提升了开发效率。所以React可以说是一个响应式的系统,并且内部会维护一个Virtual DOM,通过Diffing算法尽量复用DOM节点,提高生产环境运行效率。此外,组件化的设计模式和申明式编码也大大提高了开发效率和复用性。
React Native和Electron框架极大拓展了应用场景~
React 设计思路
React的出现是为了解决UI编程的痛点:
- 状态更新,UI不会自动更新,需要手动调用
DOM来处理更新逻辑。- 欠缺基本的代码层面的封装和隔离,代码层面没有组件化。
- UI之间数据没有依赖关系,需要手动维护,如果依赖链过长,会遇到回调地狱问题。
为了解决以上痛点,React做了以下设计:
1. 响应式与转换式
-
转换式系统:给定输入,求解输出。比如:编译器系统。 -
响应式系统:监听事件,消息驱动。比如:监控系统,UI界面等。
具体分析一下前端代码的执行逻辑会发现,很少有需要大量计算的工作,更多的时候是在监听一个事件,如果发生立马执行回调,那么显然响应式系统更加适合作为设计模式。
2. 响应式编程
- 状态更新,UI自动更新。
- 前端代码组件化,提高复用性,封装性。
- 状态之间的互相依赖关系,只需要声明即可。
3. 组件化
- 组件声明状态和UI映射。
- 组件是组件的组合,或者原子组件。
- 组件有
Props/State两种状态。 - 组件内部有私有状态
State,内部的状态外部不可见。 - 组件接受外部
Props状态提供复用;父组件可以将状态传入组件内部。 - 组件依据当前的
Props/State返回UI。
4. 状态管理
原则上组件应该将状态封装在内部,但是很多业务场景都会需要共享状态的情况。那么由于React是单向数据流,如果需要共享的状态不是父子组件,一般这种情况会把状态上升到共同父组件身上,但是会造成的后果是状态一旦更新,父组件之下的每个子组件都会执行一遍render方法,极大影响性能。因此通常的解决方式是利用状态管理库,将状态抽离到组件外部保存维护。
5. 生命周期
组件挂载第一步先执行构造器函数;然后执行render方法返回Virtual DOM节点。当发生更新的时候,会依据会传入的Props,setState和forceUpdate参数计算新的返回值。当返回的Virtual DOM会在React内部通过Diffing算法对比更改节点,尽量提高复用。
React 实现
1. JSX语法
JSX语法本身不符合js语法规范,会经过React和Babel转译成符合js语法规范的代码。如下图所示,左侧return为JSX语法代码,经过转译后如右图所示:
2. Virtual DOM
Virtual DOM是一种用于和真实DOM同步,并在js内存中维护的一个对象,它具有和DOM类似的树状结构,并且可以和DOM建立一一映射关系。它赋予了React声明式能力,使得从手动操作DOM更新中解放出来。
更新步骤大致如下:
3. Diffing 算法
最难的部分其实就是Diffing算法。希望更新次数足够少并且计算速度足够快,在这两个之间TradeOff。但是完美的最小Diffing算法,时间复杂度为O(n^3);用牺牲理论最小Diffing换取时间,可以得到O(n)复杂度的算法。
How to Diff?
遵循以下原则:
| 元素 | 方式 |
|---|---|
| 不同类型的元素 | 替换 |
同类型的DOM元素 | 更新 |
| 同类型的组件元素 | 递归 |
PS:
React中如果一个父组件状态发生改变,子组件会递归render,非常影响性能。因此,如果组件公用状态,可以用状态管理库来解决,而不必将状态上升到父节点,节约性能。
React 状态管理库
1. 核心思想
因为状态应该放在组件内部,但是如果需要状态需要共享,就需要上升节点。但是可以将状态放在组件外部,利用状态管理库来进行共享。(但是有一个严重问题:会影响组件的复用性,会使得状态与外部库强耦合,因此最好不要在开发一个库中使用这种解决方案。)
核心思想就是将状态抽离到组件外部,统一处理。
2. 推荐应用
- Redux
- xstate
- mobx
- recoil