设计理念
React是由Meta(原Facebook)开发并开源的JavaScript库,首次发布于2013年。它并非完整的框架,而是一个专注于构建用户界面的视图层库。React的核心理念可以概括为三个关键词:声明式(Declarative)、组件化(Component-Based)和一次学习,随处编写(Learn Once, Write Anywhere)。
声明式编程是React与命令式编程的根本区别。在声明式范式下,开发者描述"界面应当是什么样子",而非"如何一步步更新界面"。React负责根据状态变化高效地更新DOM,开发者无需手动操作DOM节点。这种抽象使代码更易于理解和调试,因为UI状态与数据状态之间存在明确的映射关系。
组件化是React架构的基石。用户界面被拆分为独立、可复用的组件,每个组件负责自身的渲染逻辑和状态管理。组件可以嵌套组合,形成复杂的界面结构,同时保持每个部分的独立性和可测试性。这种模块化的思维方式与软件工程中的关注点分离原则高度契合。
虚拟DOM(Virtual DOM)是React实现高效更新的关键技术。当状态变化时,React首先在内存中构建新的虚拟DOM树,与之前的虚拟DOM树进行差异比较(Diffing),计算出最小的DOM操作集合,然后将这些操作批量应用到真实DOM上。这种批处理和差异更新的策略避免了频繁的直接DOM操作带来的性能开销。
JSX 语法
JSX(JavaScript XML)是一种JavaScript的语法扩展,允许在JavaScript代码中编写类似HTML的结构。它并非模板语言,而是编译时转换为JavaScript函数调用的语法糖。每个JSX元素都会被编译为React.createElement(type, props, ...children)的调用。
JSX的语法规则包括:使用大括号{}嵌入JavaScript表达式(但不可嵌入语句,如if/for);使用camelCase命名属性(如className代替class,htmlFor代替for,因为class和for是JavaScript保留字);所有标签必须闭合(包括自闭合标签如<img />);根元素必须唯一(或使用React.Fragment/<>包裹多个同级元素)。
JSX中的条件渲染通常通过三元运算符或逻辑与运算符实现。列表渲染通过数组的map方法实现,每个列表项必须提供唯一的key属性,以帮助React识别哪些元素发生了变化、新增或移除。key应当使用稳定且唯一的标识(如数据库ID),避免使用数组索引(除非列表顺序永不改变且无增删操作)。
JSX的编译需要Babel或TypeScript等转译工具支持。现代构建工具(如Vite、Create React App)已内置JSX转换配置。React 17引入了新的JSX转换机制,无需在每个文件中显式导入React即可使用JSX,编译器会自动注入必要的运行时导入。
组件系统
React组件是独立的、可复用的代码片段,负责返回描述界面结构的JSX元素。组件分为函数组件(Function Components)和类组件(Class Components)。自React 16.8引入Hooks后,函数组件成为官方推荐的主要写法,类组件逐渐淡出主流使用。
函数组件本质上是接受props对象作为参数、返回JSX元素的纯函数(理想情况下)。这种纯函数特性使组件易于测试和推理——给定相同的输入(props和state),总是产生相同的输出(UI)。组件的命名必须以大写字母开头,以便React区分组件标签与原生HTML标签。
Props(Properties)是组件之间传递数据的机制。Props是只读的——组件不能修改接收到的props,这保证了数据流的单向性。父组件通过属性向子组件传递数据,子组件通过回调函数属性向父组件传递事件。这种单向数据流使数据变化可预测、可追踪,是React应用易于维护的关键。
组件组合优于继承是React的设计哲学。React不推荐通过继承复用组件逻辑,而是推崇通过props.children或显式的props插槽(render props模式)实现组件组合。这种模式更加灵活,且避免了继承带来的紧耦合问题。
受控组件与非受控组件是表单处理的两种模式。受控组件将表单值存储在React状态(state)中,通过onChange事件更新,React完全控制表单值。非受控组件将表单值交给DOM自身管理,通过ref获取当前值。受控组件使数据流更加清晰,便于验证和联动操作,是推荐的主流做法。
状态管理
状态(State)是组件内部可变的数据存储。与props不同,state是组件私有的,完全由组件自身控制。当state变化时,React会自动重新渲染组件及其子组件。状态管理是React应用开发的核心议题,从组件级状态到应用级状态,存在多种管理策略。
useState是函数组件中管理状态的基础Hook。它接受初始状态值,返回当前状态值和更新函数。状态更新函数接受新值或基于旧值的函数(适用于新值依赖旧值的场景)。需要注意的是,状态更新是异步且批处理的——在同一事件处理函数中多次调用状态更新,React会合并为一次重新渲染。
状态提升(Lifting State Up)是处理多个组件共享状态的标准模式。当多个组件需要共享同一状态时,应将状态移至它们共同的最近父组件中,通过props向下传递数据和回调函数。这种模式保持了单向数据流的清晰性,避免了多个数据源导致的同步问题。
对于复杂的应用级状态,React提供了Context API和useReducer Hook。Context API通过Provider/Consumer模式,使数据能够跨越多层组件直接传递给深层后代,避免了逐层传递props的"属性钻探"(Prop Drilling)问题。但Context并非为高频更新的场景设计——当Context值频繁变化时,所有消费该Context的组件都会重新渲染。
useReducer是useState的替代方案,适用于状态逻辑复杂、包含多个子值或下一个状态依赖前一个状态的场景。它接受一个reducer函数((state, action) => newState)和初始状态,返回当前状态和dispatch函数。这种模式与Redux的状态管理思想一致,适合在组件内部实现复杂的状态转换逻辑。
Hooks 机制
Hooks是React 16.8引入的革命性特性,它允许函数组件使用状态和其他React特性,彻底改变了React组件的编写方式。Hooks的设计动机包括:在组件之间复用状态逻辑困难、复杂类组件难以理解(this绑定、生命周期方法分散相关逻辑)、以及类组件难以被编译器优化。
useState和useEffect是最常用的两个Hooks。useEffect用于在函数组件中执行副作用操作(数据获取、订阅、手动DOM操作等)。它接受一个副作用函数和一个依赖数组。依赖数组中的值变化时,副作用函数会重新执行。空数组[]表示仅在组件挂载和卸载时执行(相当于componentDidMount和componentWillUnmount的组合)。省略依赖数组则每次渲染后都会执行。
useEffect的副作用函数可以返回一个清理函数(cleanup function),React会在组件卸载前或依赖变化导致重新执行前调用该清理函数。这对于订阅、定时器等需要清理的资源至关重要,能够有效防止内存泄漏。
useRef返回一个可变的引用对象,其.current属性被初始化为传入的参数。ref对象在组件的整个生命周期内保持不变,且修改ref.current不会触发重新渲染。useRef常用于访问DOM节点(将ref属性绑定到元素上)或保存不需要触发渲染的可变值(如定时器ID、上一次的状态值等)。
useMemo和useCallback是性能优化相关的Hooks。useMemo缓存计算结果,仅在依赖变化时重新计算;useCallback缓存函数引用,避免子组件因函数引用变化而不必要的重新渲染。两者都应谨慎使用——缓存本身也有开销,仅在确实遇到性能瓶颈且经过测量确认时使用,过早优化反而可能降低性能。
useLayoutEffect与useEffect签名相同,但它在浏览器绘制之前同步执行。这适用于需要读取DOM布局并同步触发重绘的场景(如测量元素尺寸并调整位置),但会阻塞视觉更新,应尽量避免使用。
自定义Hooks是将组件逻辑提取到可复用函数中的机制。自定义Hook是以use开头的函数,可以调用其他Hooks。通过自定义Hooks,可以将数据获取、表单处理、订阅管理等逻辑从组件中抽离,实现跨组件的逻辑复用,同时保持代码的清晰和可测试性。
生命周期
类组件的生命周期分为三个阶段:挂载(Mounting)、更新(Updating)和卸载(Unmounting)。挂载阶段包括constructor、static getDerivedStateFromProps、render和componentDidMount。更新阶段包括static getDerivedStateFromProps、shouldComponentUpdate、render、getSnapshotBeforeUpdate和componentDidUpdate。卸载阶段包括componentWillUnmount。
在函数组件中,生命周期的概念被Hooks重新诠释。useEffect的组合可以覆盖类组件的大部分生命周期场景:挂载和更新对应useEffect的执行,卸载对应useEffect返回的清理函数的执行。useEffect的依赖数组提供了比生命周期方法更细粒度的控制——可以精确指定哪些数据变化应当触发副作用,而非在任意更新时都执行。
错误边界(Error Boundaries)是捕获子组件树JavaScript错误的特殊组件。类组件通过componentDidCatch或static getDerivedStateFromError生命周期方法实现错误边界。当子组件抛出错误时,错误边界可以显示降级UI,防止整个应用崩溃。目前函数组件尚无直接实现错误边界的Hook,仍需使用类组件。
生态体系
React的生态系统极为丰富,涵盖了状态管理、路由、表单处理、服务端渲染、静态站点生成、测试等方方面面。React Router是React应用中最流行的路由解决方案,提供声明式的路由配置,支持动态路由、嵌套路由、路由守卫和懒加载。
Redux是React生态中最知名的状态管理库,遵循Flux架构的单向数据流原则。Redux将应用状态存储在单一的Store中,状态变化通过纯函数Reducer描述,通过Dispatch Action触发。Redux Toolkit(RTK)是官方推荐的现代Redux写法,简化了样板代码,内置了Immer以实现可变语法下的不可变更新。
Next.js是基于React的全栈框架,提供服务端渲染(SSR)、静态站点生成(SSG)、增量静态再生(ISR)、API路由、文件系统路由等能力。它抽象了构建配置的复杂性,使开发者能够专注于业务逻辑,同时获得优异的性能和SEO表现。
React的并发特性(Concurrent Features,React 18引入)包括自动批处理、Suspense改进、Transitions和新的Hooks(useId、useDeferredValue、useTransition)。这些特性使React能够更好地处理高优先级更新(如用户输入)与低优先级更新(如数据获取后的列表渲染)的调度,提升用户体验的流畅度。