第二章:React 基础概念

组件化是现代前端开发的核心理念。React 将用户界面抽象为一个个独立的组件,每个组件负责一块独立的 UI 功能。

什么是组件

组件是 UI 的独立、可复用的代码片段。从概念上看,组件就像 JavaScript 函数,接收任意的输入(称为 props),返回描述页面应该显示什么的 React 元素。

组件化的好处

  • 复用性:写好一个组件,可以在多处使用
  • 维护性:每个组件职责单一,便于理解和维护
  • 协作性:团队成员可以并行开发不同的组件
  • 测试性:独立的组件更容易测试

组件的层次结构

React 应用本质上是一棵组件树。顶层是根组件(通常是 App),下面是各种子组件。这种树状结构让数据流动清晰可控。

DOM 操作的问题

浏览器中的 DOM 操作是昂贵的。每次修改 DOM,浏览器都需要重新计算布局、重绘页面。频繁的 DOM 操作会导致页面卡顿。

虚拟 DOM 的解决方案

React 引入了虚拟 DOM(Virtual DOM)的概念。虚拟 DOM 是真实 DOM 的轻量级 JavaScript 对象表示。

工作流程:

1. 当状态变化时,React 首先更新虚拟 DOM
2. React 比较新旧虚拟 DOM(Diff 算法)
3. 计算出最小的 DOM 变更集合
4. 批量执行实际的 DOM 更新

Diff 算法

React 的 Diff 算法基于以下假设:

1. 不同类型的元素会产生不同的树
2. 开发者可以通过 key 属性提示哪些子元素是稳定的

这使得 Diff 算法的时间复杂度从 O(n³) 降低到 O(n)。

Reconciliation(协调)

协调是 React 更新 DOM 的过程。当组件的 props 或 state 变化时,React 会重新渲染组件,生成新的虚拟 DOM 树,然后与旧的虚拟 DOM 树进行比较,最后更新真实 DOM。

什么是 JSX

JSX 是一种 JavaScript 语法扩展,它让你在 JavaScript 中编写类似 HTML 的代码。

const element = <h1>Hello, world!</h1>;

这段代码看起来像 HTML,但实际上是 JavaScript。Babel 等工具会将 JSX 转换为 React.createElement() 调用。

JSX 的优点

  • 直观:在 JavaScript 中直接描述 UI 结构
  • 强大:可以在 JSX 中使用完整的 JavaScript 表达式
  • 安全:JSX 在编译时会自动转义,防止 XSS 攻击

JSX 的规则

  • 必须有一个根元素
  • 使用 className 代替 class
  • 使用 camelCase 属性名(如 onClick 而非 onclick)
  • 标签必须闭合

数据流动的方向

React 采用单向数据流(Unidirectional Data Flow):

  • 数据从父组件通过 props 流向子组件
  • 子组件通过回调函数通知父组件状态变化
  • 状态提升:将共享状态提升到最近的公共祖先组件

为什么要单向数据流

  • 可预测性:数据从哪里来、到哪里去,一目了然
  • 易调试:数据变化路径清晰,便于追踪 bug
  • 易优化:React 知道数据从哪里来,可以进行针对性的更新

状态提升示例

当多个组件需要共享状态时,将状态提升到它们的共同父组件中。父组件通过 props 将状态传递给子组件,子组件通过回调函数通知父组件更新状态。

声明式 vs 命令式

命令式编程关注“怎么做”:

// 命令式:一步一步告诉计算机怎么做
const list = document.getElementById('list');
list.innerHTML = '';
for (let i = 0; i < items.length; i++) {
  const li = document.createElement('li');
  li.textContent = items[i];
  list.appendChild(li);
}

声明式编程关注“做什么”:

// 声明式:描述界面应该是什么样子
function TodoList({ items }) {
  return (
    <ul>
      {items.map(item => <li key={item.id}>{item.text}</li>)}
    </ul>
  );
}

React 的声明式优势

  • 代码更简洁、更易读
  • UI 与状态同步自动完成
  • 减少手动 DOM 操作带来的错误

受控组件

表单元素的值由 React 组件的 state 控制。用户的输入会触发 onChange 事件,更新 state,state 的变化又会重新渲染组件。

function ControlledInput() {
  const [value, setValue] = useState('');
 
  return (
    <input 
      value={value} 
      onChange={e => setValue(e.target.value)} 
    />
  );
}

非受控组件

表单元素的值由 DOM 自身管理。通过 ref 获取 DOM 节点的值。

function UncontrolledInput() {
  const inputRef = useRef(null);
 
  const handleSubmit = () => {
    console.log(inputRef.current.value);
  };
 
  return (
    <>
      <input ref={inputRef} defaultValue="" />
      <button onClick={handleSubmit}>提交</button>
    </>
  );
}

选择建议

  • 大多数情况下使用受控组件
  • 需要直接操作 DOM 时(如文件上传)使用非受控组件
  • 简单的、一次性的表单可以使用非受控组件简化代码

浏览器事件的差异

不同浏览器的事件处理存在差异。React 通过合成事件系统(SyntheticEvent)抹平了这些差异。

合成事件的特点

  • 跨浏览器兼容
  • 事件委托:所有事件都委托到 document 或根容器
  • 事件对象被复用(React 17+ 后已改为持久化)

常用合成事件

  • 剪贴板事件:onCopy、onCut、onPaste
  • 表单事件:onChange、onInput、onSubmit
  • 焦点事件:onFocus、onBlur
  • 键盘事件:onKeyDown、onKeyPress、onKeyUp
  • 鼠标事件:onClick、onContextMenu、onDoubleClick、onDrag、onDrop
  • 触摸事件:onTouchStart、onTouchMove、onTouchEnd

首次渲染(Mount)

1. 调用组件函数/类,生成虚拟 DOM
2. 递归渲染所有子组件
3. 将虚拟 DOM 转换为真实 DOM
4. 插入到页面中
5. 执行副作用(如 useEffect)

更新渲染(Update)

1. 状态或 props 变化,触发重新渲染
2. 生成新的虚拟 DOM
3. Diff 算法比较新旧虚拟 DOM
4. 计算最小更新集合
5. 批量更新真实 DOM

卸载(Unmount)

1. 清理副作用(如 useEffect 的返回函数)
2. 从 DOM 中移除组件
3. 释放相关资源

不必要的渲染

组件的 state 或 props 变化时,React 会重新渲染组件及其所有子组件。这可能导致不必要的渲染。

优化策略

  • React.memo:缓存函数组件,props 不变时不重新渲染
  • useMemo:缓存计算结果
  • useCallback:缓存回调函数
  • shouldComponentUpdate / PureComponent:类组件优化
  • 虚拟列表:只渲染可视区域的内容

本章介绍了 React 的核心概念:组件化、虚拟 DOM、JSX、单向数据流、声明式编程等。理解这些概念是掌握 React 的基础。

在下一章,我们将开始搭建 React 开发环境,编写第一个 React 应用。

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • react/基础概念.txt
  • 最后更改: 2026/03/13 15:49
  • 张叶安