====== 第二章:React 基础概念 ======
===== 2.1 组件化思想 =====
组件化是现代前端开发的核心理念。React 将用户界面抽象为一个个独立的组件,每个组件负责一块独立的 UI 功能。
**什么是组件**:
组件是 UI 的独立、可复用的代码片段。从概念上看,组件就像 JavaScript 函数,接收任意的输入(称为 props),返回描述页面应该显示什么的 React 元素。
**组件化的好处**:
* **复用性**:写好一个组件,可以在多处使用
* **维护性**:每个组件职责单一,便于理解和维护
* **协作性**:团队成员可以并行开发不同的组件
* **测试性**:独立的组件更容易测试
**组件的层次结构**:
React 应用本质上是一棵组件树。顶层是根组件(通常是 App),下面是各种子组件。这种树状结构让数据流动清晰可控。
===== 2.2 虚拟 DOM =====
**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。
===== 2.3 JSX =====
**什么是 JSX**:
JSX 是一种 JavaScript 语法扩展,它让你在 JavaScript 中编写类似 HTML 的代码。
const element = Hello, world!
;
这段代码看起来像 HTML,但实际上是 JavaScript。Babel 等工具会将 JSX 转换为 React.createElement() 调用。
**JSX 的优点**:
* 直观:在 JavaScript 中直接描述 UI 结构
* 强大:可以在 JSX 中使用完整的 JavaScript 表达式
* 安全:JSX 在编译时会自动转义,防止 XSS 攻击
**JSX 的规则**:
* 必须有一个根元素
* 使用 className 代替 class
* 使用 camelCase 属性名(如 onClick 而非 onclick)
* 标签必须闭合
===== 2.4 单向数据流 =====
**数据流动的方向**:
React 采用单向数据流(Unidirectional Data Flow):
* 数据从父组件通过 props 流向子组件
* 子组件通过回调函数通知父组件状态变化
* 状态提升:将共享状态提升到最近的公共祖先组件
**为什么要单向数据流**:
* **可预测性**:数据从哪里来、到哪里去,一目了然
* **易调试**:数据变化路径清晰,便于追踪 bug
* **易优化**:React 知道数据从哪里来,可以进行针对性的更新
**状态提升示例**:
当多个组件需要共享状态时,将状态提升到它们的共同父组件中。父组件通过 props 将状态传递给子组件,子组件通过回调函数通知父组件更新状态。
===== 2.5 声明式编程 =====
**声明式 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 (
{items.map(item => - {item.text}
)}
);
}
**React 的声明式优势**:
* 代码更简洁、更易读
* UI 与状态同步自动完成
* 减少手动 DOM 操作带来的错误
===== 2.6 受控组件与非受控组件 =====
**受控组件**:
表单元素的值由 React 组件的 state 控制。用户的输入会触发 onChange 事件,更新 state,state 的变化又会重新渲染组件。
function ControlledInput() {
const [value, setValue] = useState('');
return (
setValue(e.target.value)}
/>
);
}
**非受控组件**:
表单元素的值由 DOM 自身管理。通过 ref 获取 DOM 节点的值。
function UncontrolledInput() {
const inputRef = useRef(null);
const handleSubmit = () => {
console.log(inputRef.current.value);
};
return (
<>
>
);
}
**选择建议**:
* 大多数情况下使用受控组件
* 需要直接操作 DOM 时(如文件上传)使用非受控组件
* 简单的、一次性的表单可以使用非受控组件简化代码
===== 2.7 合成事件 =====
**浏览器事件的差异**:
不同浏览器的事件处理存在差异。React 通过合成事件系统(SyntheticEvent)抹平了这些差异。
**合成事件的特点**:
* 跨浏览器兼容
* 事件委托:所有事件都委托到 document 或根容器
* 事件对象被复用(React 17+ 后已改为持久化)
**常用合成事件**:
* 剪贴板事件:onCopy、onCut、onPaste
* 表单事件:onChange、onInput、onSubmit
* 焦点事件:onFocus、onBlur
* 键盘事件:onKeyDown、onKeyPress、onKeyUp
* 鼠标事件:onClick、onContextMenu、onDoubleClick、onDrag、onDrop
* 触摸事件:onTouchStart、onTouchMove、onTouchEnd
===== 2.8 组件的渲染过程 =====
**首次渲染(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. 释放相关资源
===== 2.9 React 的渲染优化 =====
**不必要的渲染**:
组件的 state 或 props 变化时,React 会重新渲染组件及其所有子组件。这可能导致不必要的渲染。
**优化策略**:
* **React.memo**:缓存函数组件,props 不变时不重新渲染
* **useMemo**:缓存计算结果
* **useCallback**:缓存回调函数
* **shouldComponentUpdate / PureComponent**:类组件优化
* **虚拟列表**:只渲染可视区域的内容
===== 2.10 总结 =====
本章介绍了 React 的核心概念:组件化、虚拟 DOM、JSX、单向数据流、声明式编程等。理解这些概念是掌握 React 的基础。
在下一章,我们将开始搭建 React 开发环境,编写第一个 React 应用。