Skip to content

React 原理

仲灏2022-07-04约 1 分钟

  • 一种编程范式
  • 纯函数
  • 不可变值

vdom 和 diff 算法

  • h 函数

  • vnode 数据结构

  • patch 函数

  • 只比较同一层级,不跨级比较

  • tag 不相同,则直接删掉重建,不再深度比较

  • tag 和 key,两者都相同,则认为是相同节点,不再深度比较

JSX 本质

  • JSX 即 createElement 函数
  • 执行生成 vnode
  • patch(elem, vnode) 和 patch(vnode, newVnode)

合成事件

  • 所有事件挂载到 document 上(React17版本开始,事件就不再绑定到 document上了,放到了root组件上
  • event 不是原生的,是 SyntheticEvent 合成事件对象
  • 和 Vue 事件不同,和 DOM 事件也不同

为何要合成事件机制?

  • 更好的兼容性和跨平台

  • 载到 document,减少内存消耗,避免频繁解绑

  • 方便事件的统一管理(如事务机制)

React 17 事件绑定到 root

  • React 16 绑定到 document
  • React 17 事件绑定到 root 组件
  • 有利于多个 React 版本并存,例如微前端

setState 和 batchUpdate

  • 有时异步(普通使用),有时同步 ( setTimeout、 DOM 事件)
  • 有时合并(对象形式),有时不合并(函数形式)
  • 后者比较好理解(像 Object.assign ),主要讲解前者

核心要点

setState 主流程

image-20220704110728020

batchUpdate 机制

哪些能命中 batchUpdate 机制

  • 生命周期(和它调用的函数)
  • React 中注册的事件(和它调用的函数)
  • React 可以“管理〞的入口
transaction 事务机制
react
 * <pre>
 *                       wrappers (injected at creation time)
 *                                      +        +
 *                                      |        |
 *                    +-----------------|--------|--------------+
 *                    |                 v        |              |
 *                    |      +---------------+   |              |
 *                    |   +--|    wrapper1   |---|----+         |
 *                    |   |  +---------------+   v    |         |
 *                    |   |          +-------------+  |         |
 *                    |   |     +----|   wrapper2  |--------+   |
 *                    |   |     |    +-------------+  |     |   |
 *                    |   |     |                     |     |   |
 *                    |   v     v                     v     v   | wrapper
 *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
 * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
 * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | +---+ +---+   +---------+   +---+ +---+ |
 *                    |  initialize                    close    |
 *                    +-----------------------------------------+
 * </pre>

组件渲染过程

  • props state
  • render0 生成 vnode
  • patch (elem, vnode)

组件初始化 -> render 方法 -> 生成虚拟DOM -> React.DOM.render 方法 -> 真实DOM

组件更新 -> render 方法 -> 生成新的虚拟DOM -> diff算法 -> 定位出两次虚拟DOM的差异

render方法

image-20220704105512151

组件更新过程

  • setState(newState) --> dirtyComponents( 可能有子组件)
  • render0 生成 newVnode
  • patch(vnode, newVnode)

更新的两个阶段

  • 上述的 patch 被拆分为两个阶段:
  • reconciliation 阶段一执行 diff 算法,纯JS 计算
  • commit 阶段-将 diff 结果渲染 DOM

fiber 性能

可能会有性能问题

  • JS是单线程,且和 DOM 渲染共用一个线程
  • 当组件足够复杂,组件更新时计算和渲染都压力大
  • 同时再有 DOM 操作需求(动画,鼠标拖拽等),将卡顿

解决方案 fiber

  • 将 reconciliation 阶段进行任务拆分 (commit 无法拆分)
  • DOM 需要渲染时暂停,空闲时恢复
  • window.requestldleCallback