Skip to content
React 概述
React 的定位
React 是用于构建用户界面的 JavaScript 库。它不是一个框架:本身不提供路由、数据获取这类配套,构建完整应用时需要与 Next.js、React Router 等工具配合。
库的职责集中在声明式描述 UI、管理界面更新上——用可组合的单元(组件)拼出现状应该是什么样子,剩下 Diff、渲染、事件绑定这些事由 React 处理。
为什么需要 React
直接操作 DOM 有几个很现实的问题:
- 每一个输入框、按钮、列表项的状态都要手动维护。状态越多,步骤越多,定位 Bug 时心智负担就上来了。
- 更新界面时得先找到元素再修改属性或内容,界面复杂后“找元素→改元素”的模式会散落在各处,日后很难梳理。
- 不同的人写出来的 UI 逻辑经常无法复用——把一块界面拆成独立单元的成本太高。
React 提供了一种不同的模型:
jsx
function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
}这段代码是一个组件。它接收输入(name),返回对界面的描述。React 拿到这个描述后负责把它变成屏幕上的 DOM。当输入变化,React 会比较描述的变化,再决定怎么动 DOM。开发者不用关心“什么时候改、怎么改、哪个元素变了”,只需要给定“数据→界面”的关系。
核心理念:组件、单向数据流与虚拟 DOM
组件化:界面拆分的单元
组件就是一个普通的 JavaScript 函数(早年的类组件现在基本不推荐新写了)。它接收参数(props),返回 React 元素。函数签名已经决定了它是可组合的:组件里可以放别的组件,就像搭积木。
jsx
function VideoList({ videos }) {
return (
<ul>
{videos.map(v => (
<li key={v.id}>{v.title}</li>
))}
</ul>
);
}VideoList 只关心拿到 videos 后怎么画列表,不关心数据从哪来。这种封装性使得:
- 同样的 UI 片段可以在不同页面/位置复用。
- 修改一个组件不会意外污染其他组件。
- 测试可以单独进行。
单向数据流:数据如何驱动界面
在 React 中,数据总是从父组件流向子组件,子组件不能就地修改接收到的 props。这个约束让数据的变更路径变得单一。
如果界面需要交互,交互产生的新数据(比如用户输入)会通过回调向上通知:
jsx
function SearchBar({ query, onQueryChange }) {
return (
<input
value={query}
onChange={e => onQueryChange(e.target.value)}
/>
);
}父组件持有真正的数据(state),传递给 SearchBar 的 query 只是状态的一个快照。输入框变动的唯一结果就是回调触发,父组件更新自己的 state,React 再根据新的状态重新渲染受影响的部分。
单向流动不是框架强加的限制,而是让“此刻界面由什么数据决定”这件事始终有据可查。复杂交互下,数据每做一次更改,都能反向追踪到起点。
Virtual DOM:更新成本的控制
React 拿到组件返回的描述后,不是直接就往 DOM 上写。它内部维护一份轻量的对象树——Virtual DOM。
大致流程是:
- 组件第一次渲染时,生成一棵 Virtual DOM 节点。
- 当状态或 props 变化导致组件重新渲染时,生成一棵新的 Virtual DOM 节点。
- 协调(Reconciliation)阶段比较两棵树,找出需要更新到真实 DOM 的最小差异。
- 把这些差异一次性应用到 DOM。
这个机制的收益不在于“比操作 DOM 更快”,代码里直接操作 DOM 在特定场景下可以极快。收益在于:
- 用算法决定哪些 DOM 实际上需要变更,避免开发者手动判断。
- 将多次状态变更导致的多次渲染调用合并到一次 DOM 写入。
因为协调过程需要稳定的节点身份,所以列表中的每个节点需要一个 key 属性帮助 React 辨认哪些项目被添加、删除或调整了位置。
最小示例:组件、Props 与更新
合并上面三个概念,可以得到一个简单可运行的模型(无需完整环境也能看懂逻辑):
jsx
function App() {
const [text, setText] = React.useState('');
return (
<div>
<SearchBar
query={text}
onQueryChange={setText}
/>
<p>当前输入:{text}</p>
</div>
);
}这里 App 是顶层组件,它管理 text 这个状态。SearchBar 接收 query 和 onQueryChange 两个 props——数据向下,事件通知向上。每当输入框的值变化,setText 被调用,text 更新,App 重新渲染,p 标签的内容跟着更新。这个循环就是 React 最基础的交互模型。
React 的适用场景与边界
适合:
- 需要频繁更新、交互密集的 Web 应用(后台管理系统、协作工具、数据仪表盘)。
- 界面可拆成多个复用单元的项目(组件库、多端一致 UI)。
- 后续可能扩展为移动端应用(React Native 复用组件思想)。
- 现有 HTML 页面中想局部引入复杂交互(部分替换)。
不适合(或单独使用成本偏高):
- 纯静态内容页面。React 的运行时体积和 JSX 编译环节对纯粹的内容展示没有收益。
- 简单独立的交互(如单个表单验证)。原生 JS 或轻量库更合适。
- 对 SEO 有要求且不做服务端渲染的站点。客户端渲染的初始 HTML 往往是空壳,搜索抓取可能拿不到内容;要解决需要配合 Next.js 等框架做 SSR。
React 本身只是一个 UI 层,不包含构建、部署、路由、状态管理等基础设施。这是灵活性的来源,也意味着需要为具体项目搭配工具链。
动手:用 Vite 创建第一个 React 应用
初始化项目
确保 Node.js 与 npm 可用,在终端中执行:
bash
npm create vite@latest my-react-app -- --template react-ts命令会创建一个名为 my-react-app 的目录,内含一个基于 TypeScript 的 React 项目骨架。
接着进入项目并安装依赖:
bash
cd my-react-app
npm installVite 会处理好 JSX 的编译、模块打包和开发服务器的搭建,无需额外配置 Babel 或 webpack。
运行项目
bash
npm run dev终端会输出本机开发地址,通常是 http://localhost:5173。浏览器打开后能看到一个带有计数器按钮和链接的欢迎页,表明 React 已正常渲染。
项目结构初览
生成的关键文件和目录:
| 路径 | 职责 |
|---|---|
index.html | 应用的 HTML 入口,内含 <div id="root"> |
src/main.tsx | JavaScript 入口,将根组件挂载到 #root |
src/App.tsx | 默认的根组件,包含计数器演示逻辑 |
src/App.css | 根组件的样式 |
src/index.css | 全局样式 |
public/ | 存放不参与编译的静态资源(如 favicon) |
package.json | 依赖声明与 npm 脚本 |
tsconfig.json | TypeScript 配置 |
vite.config.ts | Vite 配置文件 |
src/main.tsx 做了两件事:
tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)StrictMode 是开发阶段的辅助组件,会额外检查潜在问题,生产构建时会被剔除。
src/App.tsx 中的计数器演示了 useState 触发的更新流程:点击按钮改变状态,React 重新渲染显示新的计数值。我们不在此展开 Hook 的细节,但可以观察到:
- 界面是组件拼出来的(
App组件包含按钮、图片和文本)。 - 状态变更驱动了重新渲染。
- JSX 在文件中直接书写,和渲染逻辑放在一起。
至此,React 的核心闭环——组件、状态、重新渲染——在本地跑了起来。
本篇回顾
- React 是一个构建用户界面的 JavaScript 库,不是框架。
- 组件化把 UI 拆成可复用的单元,组件之间可任意组合。
- 单向数据流保证了数据变化的可追踪性,交互通过 prop 向下、回调向上完成。
- Virtual DOM 减少不必要的 DOM 操作,让更新策略由框架计算而非手动控制。
- React 擅长复杂交互场景,不适合纯静态内容或简单孤立交互。
- 用 Vite 可以在几秒钟内启动一个 React 项目,项目结构清晰,入口、组件、静态资源各归其位。
