本篇文章是专栏的第一篇文章,整合React入门所有必备基础知识点,彻底解决新手无从下手、知识点零散的问题。
首先对比传统JS开发与React声明式开发的差异,讲解React组件化 、虚拟DOM 、单向数据流三大核心设计思想。项目搭建方式(Vite+React),适配新版React开发环境,详解项目目录结构、核心配置文件作用。批量整合JSX语法全规则、表达式渲染、条件渲染、列表渲染、样式绑定、事件处理、this指向等入门核心知识点,搭配极简案例快速吃透基础语法。最后实战接入Ant Design组件库,完成安装、按需引入、主题简单配置,通过按钮、输入框、卡片等基础组件搭建首个React页面,让新手零基础实现搭建项目、写代码、UI美化完整闭环,快速建立React开发认知。
1. 为什么学 React?从命令式到声明式
传统JavaScript操作DOM属于命令式编程 ,你需要逐步告诉浏览器先找元素、再改属性、再绑事件。页面越复杂,代码越难维护。
React采用声明式编程,你只需描述UI 应该长什么样,React负责把数据映射到界面并高效更新。
1.1 传统JS与React对比
| 维度 | 传统 JS 操作 DOM | React 声明式开发 |
|---|---|---|
| 编程范式 | 命令式,一步步操作DOM | 声明式,描述UI=f(state) |
| 代码组织 | 按页面区域散落脚本 | 按组件拆分,高内聚低耦合 |
| 状态管理 | 手动同步DOM与数据 | 状态变化自动触发重渲染 |
| 更新效率 | 容易全量操作DOM | 虚拟DOM Diff,最小化更新 |
| 可维护性 | 页面越大越难改 | 组件复用,结构清晰 |
| 学习曲线 | 入门简单,工程化难 | 概念稍多,但体系化 |
1.2 传统JS写法示例
javascript
// 命令式手动创建、插入、更新DOM
const btn = document.createElement('button');
btn.textContent = '计数:0';
let count = 0;
btn.addEventListener('click', () => {
count++;
> btn.textContent = `计数:${count}`; // 必须手动同步UI
});
document.getElementById('app').appendChild(btn);
在这段代码里,count是数据,btn.textContent是视图,两者必须由开发者手动保持一致。每增加一个UI元素,就要写更多DOM API,逻辑与视图逐渐耦合成一团。当页面有10个地方依赖count 时,只要漏改一处,界面就会和数据对不上,Bug往往难以追踪。
1.3 React写法示例
jsx
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button type="button" onClick={() => setCount(count + 1)}>
计数:{count}
</button>
);
}
React写法把关注点从怎么改DOM转成了数据是什么、界面应如何呈现。count是唯一数据源,JSX中的{count}会自动渲染到按钮文字;用户点击时只需调用setCount,React会对比前后差异并更新DOM,无需再手动修改textContent。同时,整个计数逻辑被封装在Counter组件里,可以在任意页面复用,维护成本随页面增大而线性增长,而非指数爆炸。 将上述对比如图所示:

2. React 三大核心设计思想
2.1 组件化Component
React把UI拆成独立、可复用的组件,像搭积木一样组合页面。每个组件负责一块界面及其交互逻辑,父组件通过组合子组件拼出完整页面。如下图所示:

从优势上说,首先是代码复用能力,同一个Button这类封装好的组件能够在项目内多个页面、多个场景直接调用,避免重复编码。其次实现了逻辑与样式的隔离,各个组件独立维护自身的业务逻辑、状态和样式,互不干扰,降低代码耦合度。同时便于团队协同开发,开发人员可以分工负责不同组件的编写,实现并行开发,大幅提升项目推进效率。此外独立封装的组件支持单独进行单元测试,能够精准定位问题,有效降低整体测试与后期维护的成本。
jsx
// 函数组件,现代React推荐写法
function Welcome({ name }) {
return <h1>你好,{name}</h1>;
}
function App() {
return (
<div>
<Welcome name="小明" />
<Welcome name="小红" />
</div>
);
}
Welcome是一个函数组件,通过参数{ name }接收外部传入的props,并返回一段JSX。同一个组件传入不同的name,就能渲染不同的问候语,这就是组件复用的核心。现代React18及以上官方推荐函数组件+Hooks的组合方式,类组件已成为legacy写法,新项目无需再深入掌握。
2.2 虚拟DOM
React在内存中维护一棵轻量级的Virtual DOM树 。当state发生变化时,React会先生成一棵新的虚拟DOM,再与旧树做Diff 对比,最后只对真实DOM做最小化Patch,避免全量重绘带来的性能损耗。
直接操作真实DOM的代价较高,而虚拟DOM本质上是普通JavaScript对象,在内存中对比速度极快。React 19仍在通过编译器React Compiler做进一步优化,但理解Diff思想对排查渲染性能问题依然有帮助。对开发者来说,你不需要 也不应该手动操作虚拟DOM,只需维护好state与JSX的对应关系,更新工作交给React即可。
2.3 单向数据流
React中,数据从父组件经props自上而下流向子组件;子组件若需改变数据,则通过父组件传入的回调函数通知上层更新state,形成清晰、可追踪的数据环路。
单向数据流的最大价值在于可预测。任何时候你都可以沿着props的传递路径,找到数据的来源和变更点,而不必担心子组件在暗中修改父级状态。复杂应用可以在此基础上引入Context、Redux等状态管理方案,但底层思想始终是数据自上而下、事件自下而上,这条原则不会变。
3. 环境搭建Vite+React项目创建
3.1 前置要求
| 工具 | 推荐版本 | 用途 |
|---|---|---|
| Node.js | ≥ 18 | 运行 npm、Vite |
| npm / pnpm | 最新 LTS | 包管理 |
| VS Code | 任意 | 编辑器(推荐装 ESLint 插件) |
验证环境:
bash
node -v # 应输出v18.x或更高
npm -v
运行上述命令后,若Node版本低于18,建议到nodejs.org安装LTS版本Node是JavaScript的运行环境,npm随Node一起安装,用于下载和管理项目依赖。
3.2 创建项目
bash
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm install
npm run dev
npm create vite@latest会调用Vite官方脚手架,在本地生成项目骨架,无需全局安装Vite。--template react-ts指定使用React+TypeScript 模板,编辑器会提供类型提示,能在编码阶段就发现不少低级错误。进入目录后执行npm install安装依赖,再执行npm run dev启动开发服务器,浏览器访问http://localhost:5173即可看到默认页面。
ps:使用webstorm直接能创建,像下图一样选择一下就好了:

3.3 Vite开发时序
| 阶段 | 说明 |
|---|---|
| 冷启动 | 不打包,直接启动Dev Server,秒级启动 |
| 按需编译 | 浏览器请求哪个模块,Vite才编译哪个 |
| HMR | 修改代码后热替换,保留组件状态 |
| 生产构建 | npm run build使用Rolldown/Rollup打包优化 |
Vite的开发体验之所以快,是因为它跳过了传统工具先打包再启动的步骤,改为按需编译+WebSocket热更新。你修改App.tsx并保存后,浏览器会在毫秒级内局部刷新,且尽量保留组件内部状态例如输入框里已输入的文字。原理如下时序图所示:
4. 项目目录结构与核心配置
4.1 标准目录结构
以本文章用来测试案例的目录结构举例:
csharp
react_demo/
├── public/ # 静态资源,原样复制到dist
├── src/
│ ├── assets/ # 图片、字体等需import的资源
│ ├── App.tsx # 根组件
│ ├── main.tsx # 应用入口
│ └── index.css # 全局样式
├── demo/ # 本篇配套示例代码
│ ├── examples/ # JSX/事件/列表示例
│ └── antd-page/ # Ant Design实战页面
├── index.html # HTML 模板
├── vite.config.ts # Vite 配置
├── tsconfig.json # TypeScript 配置
└── package.json # 依赖与脚本
可以把这套结构理解为三层:public/和index.html负责页面骨架;src/是日常开发的主战场;根目录下的配置文件则控制构建、类型检查和依赖管理。新手不必一次记牢每个文件,但应知道改界面去src/,改构建去vite.config.ts。
4.2 核心文件说明
| 文件 | 作用 |
|---|---|
index.html |
页面骨架,含<div id="root">挂载点 |
src/main.tsx |
调用createRoot把React树渲染到 DOM |
src/App.tsx |
根组件,组织页面结构 |
vite.config.ts |
插件、别名、代理等构建配置 |
package.json |
依赖版本与dev/build脚本 |
4.3 入口文件 main.tsx
tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App.tsx';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);
这段代码是React应用的点火程序。createRoot是React 18引入的新API,取代了旧版的ReactDOM.render,支持并发渲染等现代特性。document.getElementById('root')找到index.html中的挂载节点,末尾的!是TypeScript的非空断言,告诉编译器这里一定有元素。最外层包裹的StrictMode只在开发环境生效,会额外检查过时API和潜在副作用,帮助尽早发现问题,但不会影响生产环境的包体积。
4.4 Vite配置vite.config.ts
ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});
Vite配置文件默认非常精简:@vitejs/plugin-react插件负责把JSX/TSX转成浏览器可执行的JavaScript,并启用Fast Refresh热更新。随着项目变大,你可以在这里添加路径别名例如@/ 指向src/、开发环境API代理、生产环境分包策略等,但入门阶段保持默认即可正常运行。
5. JSX语法规则
JSX是JavaScript的语法扩展,让你在JS文件中写类似HTML的结构。它并不是HTML本身,而是会被编译成React.createElement调用。
5.1 基础JSX示例
jsx
const name = 'React新手';
function App() {
return (
<>
{/* Fragment 避免多余DOM包裹 */}
<h1 className="title">Hello React</h1>
<p>欢迎学习{name},年份{new Date().getFullYear()}</p>
</>
);
}
示例中className对应HTML的class,因为class在JavaScript中是保留字,React必须换用className。花括号{}内可以写任意JavaScript表达式 ,变量、运算、函数调用都可以,但不能直接写if、for等语句。最外层的<>...</>是Fragment的简写,它把多个子节点包在一起,却不会在真实DOM中多生成一层<div>,适合并列渲染多个元素时使用。
5.2 JSX编译原理
jsx
// 你写的JSX
<h1 className="title">Hello</h1>
// 编译后等价于
React.createElement('h1', { className: 'title' }, 'Hello');
Vite在开发和构建阶段会自动完成JSX到createElement的转换,日常开发无需手写后者。了解这一层编译关系,有助于阅读报错堆栈、对照旧版教程,以及在极少数需要脱离JSX的场景下写出等价代码。
5.3 JSX规则表
| 规则 | 正确 | 错误 |
|---|---|---|
| 根节点 | <><div/><div/></>或单根<div> |
多个并列根节点 |
| 属性名 | className htmlFor |
class for |
| 自闭合 | <img /> <input /> |
<img>无闭合 |
| 表达式 | {user.name} |
"user.name"字符串 |
| 注释 | {/* 注释 */} |
<!-- --> |
| 样式 | style={{ color: 'red' }} |
style="color:red" |
| 布尔属性 | disabled或disabled={true} |
--- |
6. 表达式、条件与列表渲染
6.1 表达式渲染
jsx
const price = 99;
const discount = 0.8;
<p>
原价 {price} 元,折后 {price * discount} 元
</p>
JSX花括号内的内容会被当作JavaScript表达式求值,再将结果渲染到页面上。因此你不仅可以插入变量,还可以写算术运算、三元表达式、函数调用等。最终值会被转为字符串或React节点;若表达式结果为null、undefined或布尔值,React会按规则决定是否显示布尔值本身不会渲染到DOM中。
6.2 条件渲染
| 方式 | 适用场景 | 示例 |
|---|---|---|
| 三元运算符 | 二选一 | {ok ? <Success /> : <Error />} |
逻辑与&& |
有则显示 | {msg && <Alert text={msg} />} |
| 提前return | 分支复杂 | 函数开头 if (!data) return null |
| 变量存JSX | 多分支 | let content; if... |
看一下下面的代码:
jsx
function StatusBadge({ status }) {
return (
<span>
{status === 'loading' && '加载中...'}
{status === 'success' ? '成功' : status === 'error' ? '失败' : null}
</span>
);
}
条件渲染的本质是,在JSX中根据数据决定渲染什么或是否渲染。三元运算符适合在两种UI之间二选一;&&适合条件成立才显示的场景,但要注意左侧为0 时,0会被渲染到页面上,这是新手常见的坑。当分支逻辑较多时,建议提前用变量或独立子组件承载JSX,避免在return中嵌套过深的三元表达式,影响可读性。
6.3 列表渲染
jsx
const todos = [
{ id: 1, text: '学习JSX', done: true },
{ id: 2, text: '接入Ant Design', done: false },
];
<ul>
{todos.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
列表渲染通常配合数组的map方法,对每一项返回一段JSX,React会依次渲染成列表。每个列表项必须提供 key属性 ,且key应在列表范围内唯一、稳定,帮助Diff算法识别节点的增删和移动。推荐使用数据库返回的id;若没有稳定id,可以用多个字段组合生成,但应避免使用随机数,也不要在列表会重排序时使用数组下标作为key。

7. 样式绑定与事件处理
7.1 三种常用样式方式
| 方式 | 写法 | 特点 |
|---|---|---|
| 内联样式 | style={{ color: 'red' }} |
动态方便,对象camelCase |
| 全局CSS | import './App.css' |
简单,需注意类名冲突 |
| CSS Modules | import styles from './X.module.css' |
类名局部作用域,推荐 |
7.2 内联样式与className
jsx
<div
className="card active"
style={{
padding: '16px',
backgroundColor: isActive ? '#e6f4ff' : '#fafafa',
}}
>
内容
</div>
React中内联style接收的是一个JavaScript对象,属性名采用camelCase写法,例如backgroundColor而不是background-color。静态、可复用的样式建议通过className配合CSS文件管理;需要根据state动态变化的样式,则适合用内联对象或CSS变量实现。两种方式可以组合使用class负责布局和通用外观,内联style负责随状态切换的颜色、尺寸等。
7.3 事件处理
jsx
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => setCount((prev) => prev + 1);
return (
<button type="button" onClick={handleClick}>
计数:{count}
</button>
);
}
React事件名使用camelCase,例如onClick、onChange、onSubmit,传参时应传递函数引用 handleClick,而不是handleClick(),后者会在渲染阶段立刻执行,而不是等用户点击。若需要向处理函数传递参数,应写成onClick={() => handleDelete(id)}的形式,由箭头函数在事件触发时再调用。示例中setCount((prev) => prev + 1)采用函数式更新,能确保基于最新state递增,避免闭包捕获旧值的问题。
7.4 状态更新与UI同步
jsx
// 推荐基于上一次状态更新避免闭包陈旧值
setCount((prev) => prev + 1);
// 对象state需展开复制,不可直接mutate
setUser((prev) => ({ ...prev, name: '新名字' }));
调用setState后,React会把组件标记为需要更新,并在合适的时机重新执行组件函数、生成新JSX、对比差异并刷新DOM,UI与state由此保持同步。更新对象或数组类型的state时,必须创建新的引用例如用展开运算符{ ...prev, name: '新名字' }),不能直接修改原对象,否则React无法检测到变化,界面就不会更新。
8. 函数组件与this指向
8.1 为什么现代React不强调this?
| 组件类型 | this问题 | 现状 |
|---|---|---|
| 类组件 | 需bind/箭头函数/类属性 |
legacy,新项目少用 |
| 函数组件 | 无this,闭包直接访问变量 | 官方推荐 |
类组件事件绑定了解即可,不必深究:
jsx
// 类组件this指向易错
class OldCounter extends React.Component {
state = { count: 0 };
// 方法1构造函数bind
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render() {
return <button onClick={this.handleClick}>{this.state.count}</button>;
}
}
类组件中的普通方法在作为事件回调传递时,this会丢失绑定,因此不得不在构造函数里bind,或改用箭头函数、类属性写法。这套机制增加了心智负担,也是许多新手在类组件阶段踩坑的原因。函数组件配合Hooks后,组件内直接通过闭包访问变量和setState,完全不存在this指向问题,因此官方已将函数组件定为默认推荐写法。
8.2 函数组件+useState标准写法
jsx
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// 闭包中的count、setCount由React保证正确
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
函数组件每次渲染都会重新执行函数体,但useState返回的state会在多次渲染之间持久保存 ,不会因为函数重新执行而重置。React在内部维护了state与组件实例的对应关系,你只需在每次渲染中读取当前count、在事件中调用setCount即可。
9. Ant Design 实战:首个页面
Ant Design是蚂蚁金服开源的企业级React UI库,组件丰富、文档完善,适合快速搭建美观、规范的界面。
9.1 接入流程
整体接入路径很清晰:安装依赖、按需import组件、用ConfigProvider配置主题、在页面中组合使用。Vite基于原生ESM,天然支持Tree-shaking,不必再配置babel-plugin-import之类的按需加载插件。
9.2 安装
在本项目根目录执行:
bash
npm install antd
执行后,antd会写入package.json的dependencies。Vite 在生产构建时会分析import关系,只打包实际用到的组件代码,未引用的组件不会进入最终产物,因此直接按名import即可,既简单又高效。
9.3 按需引入组件
tsx
// 只引入用到的组件,减小打包体积
import { Button, Input, Card, ConfigProvider, theme } from 'antd';
应始终按需引入具体组件,避免import antd from 'antd'式的全量导入。若需要使用图标,还需单独安装并引入@ant-design/icons,例如import { UserOutlined } from '@ant-design/icons',图标库与组件库分开发布,可按需加载。
9.4 主题简单配置
通过ConfigProvider的theme属性定制主色、圆角等Design Token:
tsx
<ConfigProvider
theme={{
algorithm: theme.defaultAlgorithm, // 亮色主题
token: {
colorPrimary: '#1677ff', // 品牌主色
borderRadius: 8, // 全局圆角
},
}}
>
<App />
</ConfigProvider>
ConfigProvider是Ant Design 5的主题入口,包裹在应用最外层后,其theme.token中定义的变量会向下传递给所有子组件。修改colorPrimary后,主按钮、链接、选中等元素的主色会一并更新,无需逐个组件改样式。若需要暗色模式,将algorithm换成theme.darkAlgorithm即可切换整套配色算法。
9.5 完整实战页面
tsx
import { useState } from 'react';
import { Button, Card, ConfigProvider, Input, Space, Typography, theme } from 'antd';
const { Title, Paragraph } = Typography;
function DemoPage() {
const [username, setUsername] = useState('');
const [greeting, setGreeting] = useState('');
const handleSubmit = () => {
setGreeting(
username.trim()
? `${username.trim()}!欢迎开始React之旅。`
: '请先输入昵称'
);
};
return (
<ConfigProvider
theme={{
algorithm: theme.defaultAlgorithm,
token: { colorPrimary: '#1677ff', borderRadius: 8 },
}}
>
<div style={{ maxWidth: 480, margin: '48px auto', padding: '0 16px' }}>
<Card title="React + Ant Design 首个页面" bordered={false}>
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
<Title level={4}>零基础通关实战</Title>
<Paragraph type="secondary">
输入昵称后点击按钮,体验状态驱动UI更新。
</Paragraph>
<Input
placeholder="请输入昵称"
value={username}
onChange={(e) => setUsername(e.target.value)}
onPressEnter={handleSubmit}
allowClear
/>
<Button type="primary" block onClick={handleSubmit}>
生成问候语
</Button>
{greeting && (
<Card size="small" style={{ background: '#f6ffed', borderColor: '#b7eb8f' }}>
{greeting}
</Card>
)}
</Space>
</Card>
</div>
</ConfigProvider>
);
}
export default DemoPage;
实现的页面图片:

页面用两个独立的useState分别管理输入框内容username和问候语greeting,职责清晰。Input同时设置了value和onChange,构成受控组件 ,输入框显示的值完全由React state驱动,用户每次键入都会通过onChange回写state,再由React重新渲染输入框。onPressEnter让用户按回车即可提交,与点击按钮效果一致。Button的type="primary"和block分别表示主按钮样式和块级占满宽度;Space direction="vertical"则统一了内部元素的垂直间距,避免手动写margin。
当greeting有值时,{greeting && <Card>...}才会渲染结果卡片,这是条件渲染在实战中的典型用法。整个页面外层用Card包裹,内层再用小卡片展示问候语,层次分明,也是Ant Design推荐的卡片式布局思路。
9.7 页面交互时序
从时序上看,用户的每次输入都会先更新username,点击按钮后再根据输入内容计算并写入greeting,state变化触发重渲染,结果卡片随之出现或更新。整条链路与前文介绍的单向数据流完全一致:事件向上传递、state驱动视图向下刷新。