Skip to content

第二章 JSX

JSX 基础

1. JSX 表达式

  • 只支持 JavaScript 表达式:JSX 中只能嵌入 JavaScript 表达式,不能使用语句(如 iffor 等)。
  • createRoot 的限制createRoot 只能指定 body 的子节点作为根容器(root)。
  • 根容器与根元素的区别
    • 根容器:React 应用的挂载点(如 document.getElementById('root'))。
    • 根元素:JSX 中的最外层元素(如 <div><React.Fragment>)。

2. React.Fragment

  • <></>:空文档标记标签,提供了根元素,但不会在 DOM 中占据具体位置。
  • 应用场景:当需要返回多个元素但不想添加额外的 DOM 节点时使用。

3. Mustache 语法 {}

  • 原始值
    • 支持 stringnumber,其他数据类型(如 booleannullundefined)渲染为空。
  • 对象
    • 支持数组渲染,数组会被 flat 后依次渲染。
    • 不支持普通对象渲染(如 { key: value }),会抛出错误。
  • JSX VDOM 渲染
    • JSX 会被转换为虚拟 DOM(VDOM),最终渲染为真实 DOM。
  • style 行内样式
    • style 中可以使用 JavaScript 表达式,如三目运算符。

4. map 操作与 key

  • map 操作:通过 map 遍历数据模型(Model)时,需要为循环元素的根元素添加 key
    • key 的作用:帮助 React 识别哪些元素发生了变化,从而优化 DOM Diff 算法。
    • key 的选择:通常使用 index 或数据项的唯一 ID。
  • 示例
    jsx
    const items = [{ id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }];
    const list = items.map(item => <li key={item.id}>{item.name}</li>);

5. 密集数组与稀疏数组

  • 密集数组:每一项都有值(即使是 null),可以通过 new Array(n).fill(null) 创建。
  • 稀疏数组:每一项都是 empty,无法直接遍历。
  • 应用场景:当需要创建固定长度的占位符时,使用密集数组。

JSX 机制

1. JSX 的本质

  • 语法转换:JSX 本质上是语法糖,会被 Babel 转换为 React.createElement 调用。
  • 流程
    1. 创建 VDOM:JSX 被转换为虚拟 DOM(VDOM)。
    2. 转换为真实 DOM:VDOM 被渲染为真实 DOM。

2. 渲染过程

  • 第一次渲染
    • 缓存 VDOM。
    • 直接将 VDOM 转换为真实 DOM。
  • 后续更新
    • 通过 Diff 算法比对新旧 VDOM,计算出差异(PATCH 包)。
    • 只更新差异部分(PATCH 补丁包),避免全量更新。

3. 技术延伸

  • 虚拟 DOM 的优势
    • 性能优化:减少直接操作真实 DOM 的次数,避免重绘和回流。
    • 跨平台:VDOM 可以渲染到不同平台(如 Web、Native)。
  • 类比
    • VDOM 类似于 Git 的差异比较:只更新变化的部分,而不是全量替换。

容易忽略的问题

1. key 的重要性

  • 如果没有为列表项添加 key,React 会默认使用 index,这可能导致性能问题或渲染错误。
  • 最佳实践:始终使用唯一标识符作为 key

2. JSX 中的表达式限制

  • JSX 中只能使用表达式,不能使用语句。如果需要条件渲染,可以使用三元运算符或逻辑与(&&)。

3. 稀疏数组的陷阱

  • 稀疏数组无法直接遍历,可能导致意外行为。建议使用密集数组替代。

总结

  • JSX 是 React 的核心语法,本质上是 JavaScript 的语法糖,最终会被转换为 React.createElement 调用。
  • 虚拟 DOM 是 React 性能优化的关键,通过 Diff 算法减少不必要的 DOM 操作。
  • key 的作用不可忽视,它帮助 React 高效地更新列表项。
  • 密集数组与稀疏数组的区别:密集数组更适合用于占位符场景。