====== 第二章: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 ( ); } **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 应用。