React进阶-虚拟 DOM 和 Diff 算法

目录

1. 虚拟 DOM

2. React JSX 语法转化的过程 

3. Diff 算法的说明

Diff 算法的示例 - 1

Diff 算法的示例 - 2

Diff 算法的示例 - 3

key 属性

4. 虚拟 DOM 的真正价值

5. React Fiber

浏览器刷新频率与 JS 运行时间

React 15 的虚拟 DOM 和 Diff

React 16 的 Fiber 架构

Concurrent Mode

双缓存 Fiber tree

浏览器的 requestIdleCallback API

6.useState 模拟实现

模拟实现 useState

模拟实现 useEffect 

1. 虚拟 DOM

虚拟 DOM(Virtual DOM),就是一个 JS 对象,用来描述我们希望在页面中看到的 HTML 结构内容.

为什么使用虚拟 DOM? 真实 DOM 对象属性多,处理起来繁琐、效率低。更重要的原因:React 要做跨平台开发,而不是被束缚在浏览器端。

  • 原生 DOM 对象: 也是一个 JS 对象,是浏览器默认提供的
  • DOM 对象 和 HTML 元素之间是一一对应的关系
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello JSX!'
  }
}

// JSX

const d1 = <div></div>
// 虚拟DOM => { type: 'div' }
// const divDOM = document.createElement('div')

<div>123</div>
// 虚拟DOM => { type: 'div', props: { chilren: '123' } }

<div className="tab">123</div>
// 虚拟DOM => { type: 'div', props: { className: 'tab', chilren: '123' } }

2. React JSX 语法转化的过程 

  • 转化过程:JSX => React.createEelement() 或 _jsxRuntime => 虚拟 DOM ----> DOM -> HTML
// JSX
const el = <div className="abc" onClick={() => {}}>123</div>

// 旧的转化方式:
// React 元素
const el = /*#__PURE__*/ React.createElement(
  "div",
  {
    className: "abc",
    onClick: () => {}
  },
  "123"
);

// 新的转化方式:
var _jsxRuntime = require("react/jsx-runtime");
const el = /*#__PURE__*/ (0, _jsxRuntime.jsx)("div", {
  className: "abc",
  onClick: () => {},
  children: "123"
});

// 虚拟 DOM
{
  type: 'div',
  props: {
    className: "abc",
    onClick: () => {},
    children: "123"
  }
}

3. Diff 算法的说明

  • 第一次页面渲染的过程:1 JSX + state => 2 虚拟 DOM 树(JS 对象) => 3 浏览器中看到的 HTML 结构内容
  • 当更新了状态,就会重新渲染组件,也就会重新生成一棵新的 虚拟 DOM 树
  • Diff 算法就会:对比 初始虚拟 DOM 树 和 更新后的虚拟 DOM 树,找到不同之处,最终,只将不同的地方更新到页面中
  • 在 React 中最多会同时存在两个虚拟 DOM 对象:最新的虚拟 DOM 和 上一次的虚拟 DOM
  • 注意:只要状态更新,组件就会重新“渲染”。此处的渲染执行的是:
    1. 组件中的代码重新执行
    2. 重新生成新的虚拟 DOM 对象
    3. 进行一次 Diff
  • 注意:这个过程都是 JS 操作,不涉及 DOM 操作。最终,只将变化的内容更新到浏览器中(这是 DOM 操作)。

  • 注意:render 重新执行,不代表把整个组件重新渲染到页面中。而实际上,React 内部会使用 虚拟 DOM 和 Diff 算法来做到 部分更新
    • 部分更新(打补丁):只将变化的地方重新渲染到页面中,这样可以减少 DOM 操作

参考:React 文档 - 协调

Diff 算法的示例 - 1

  • 如果两棵树的根元素类型不同,React 会销毁旧树,创建新树
// 旧树
<div>
  <Counter />
</div>

// 新树
<span>
  <Counter />
</span>

执行过程:destory Counter -> insert Counter

Diff 算法的示例 - 2

  • 对于类型相同的 React DOM 元素,React 会对比两者的属性是否相同,只更新不同的属性
  • 当处理完这个 DOM 节点,React 就会递归处理子节点。
// 旧
<div className="before" title="stuff"></div>
// 新
<div className="after" title="stuff"></div>
只更新:className 属性

// 旧
<div style={
  {color: 'red', fontWeight: 'bold'}}></div>
// 新
<div style={
  {color: 'green', fontWeight: 'bold'}}></div>
只更新:color属性

Diff 算法的示例 - 3

  • 1 当在子节点的后面添加一个节点,这时候两棵树的转化工作执行的很好
// 旧
<ul>
  <li>first</li>
  <li>second</li>
</ul>

// 新
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

执行过程:
React会匹配新旧两个<li>first</li>,匹配两个<li>second</li>,然后添加 <li&g
  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值