告别知识点零散!React零基础通关,从环境搭建到Ant Design页面实战

  本篇文章是专栏的第一篇文章,整合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,避免全量重绘带来的性能损耗。

flowchart LR A[State变化] --> B[生成新VDOM] B --> C[Diff对比] C --> D[Patch更新] D --> E[真实DOM]

  直接操作真实DOM的代价较高,而虚拟DOM本质上是普通JavaScript对象,在内存中对比速度极快。React 19仍在通过编译器React Compiler做进一步优化,但理解Diff思想对排查渲染性能问题依然有帮助。对开发者来说,你不需要不应该手动操作虚拟DOM,只需维护好state与JSX的对应关系,更新工作交给React即可。

2.3 单向数据流

  React中,数据从父组件经props自上而下流向子组件;子组件若需改变数据,则通过父组件传入的回调函数通知上层更新state,形成清晰、可追踪的数据环路。

sequenceDiagram participant S as State participant V as View participant U as User S->>V: props驱动渲染 U->>V: 点击/输入等交互 V->>S: 调用setState S->>V: 重新渲染

  单向数据流的最大价值在于可预测。任何时候你都可以沿着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并保存后,浏览器会在毫秒级内局部刷新,且尽量保留组件内部状态例如输入框里已输入的文字。原理如下时序图所示:

sequenceDiagram participant Dev as 开发者 participant Vite as Vite Dev Server participant Browser as 浏览器 Dev->>Vite: 保存源码 Vite->>Vite: 检测变更、编译模块 Vite->>Browser: WebSocket 推送 HMR Browser->>Browser: 局部热替换

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表达式 ,变量、运算、函数调用都可以,但不能直接写iffor等语句。最外层的<>...</>是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"
布尔属性 disableddisabled={true} ---

6. 表达式、条件与列表渲染

6.1 表达式渲染

jsx 复制代码
const price = 99;
const discount = 0.8;

<p>
  原价 {price} 元,折后 {price * discount} 元
</p>

  JSX花括号内的内容会被当作JavaScript表达式求值,再将结果渲染到页面上。因此你不仅可以插入变量,还可以写算术运算、三元表达式、函数调用等。最终值会被转为字符串或React节点;若表达式结果为nullundefined或布尔值,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,例如onClickonChangeonSubmit,传参时应传递函数引用 handleClick,而不是handleClick(),后者会在渲染阶段立刻执行,而不是等用户点击。若需要向处理函数传递参数,应写成onClick={() => handleDelete(id)}的形式,由箭头函数在事件触发时再调用。示例中setCount((prev) => prev + 1)采用函数式更新,能确保基于最新state递增,避免闭包捕获旧值的问题。

sequenceDiagram participant User as 用户 participant Btn as Button participant Handler as handleClick participant State as count state User->>Btn: 点击 Btn->>Handler: 触发 onClick Handler->>State: setCount(prev + 1) State->>Btn: 重新渲染显示新 count

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.jsondependencies。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 主题简单配置

  通过ConfigProvidertheme属性定制主色、圆角等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同时设置了valueonChange,构成受控组件 ,输入框显示的值完全由React state驱动,用户每次键入都会通过onChange回写state,再由React重新渲染输入框。onPressEnter让用户按回车即可提交,与点击按钮效果一致。Buttontype="primary"block分别表示主按钮样式和块级占满宽度;Space direction="vertical"则统一了内部元素的垂直间距,避免手动写margin。

  当greeting有值时,{greeting && <Card>...}才会渲染结果卡片,这是条件渲染在实战中的典型用法。整个页面外层用Card包裹,内层再用小卡片展示问候语,层次分明,也是Ant Design推荐的卡片式布局思路。

9.7 页面交互时序

sequenceDiagram participant U as 用户 participant I as Input participant S as State participant B as Button participant C as Card U->>I: 输入昵称 I->>S: onChange 更新 username U->>B: 点击「生成问候语」 B->>S: handleSubmit 更新 greeting S->>C: greeting 有值,渲染结果卡片

  从时序上看,用户的每次输入都会先更新username,点击按钮后再根据输入内容计算并写入greeting,state变化触发重渲染,结果卡片随之出现或更新。整条链路与前文介绍的单向数据流完全一致:事件向上传递、state驱动视图向下刷新。

10. 本文小结

10.1 知识地图

mindmap root((React零基础)) 核心思想 组件化 虚拟DOM 单向数据流 工程化 Vite脚手架 目录与配置 基础语法 JSX规则 条件/列表渲染 事件与样式 UI实战 Ant Design安装 ConfigProvider主题 首个完整页面
相关推荐
cidy_982 小时前
水龙头领不到测试币?手把手用 Hardhat 本地环境零门槛学以太坊交易
前端
因_崔斯汀2 小时前
Three.js 3D 地图特效与材质实现指南
前端
angerdream2 小时前
手把手编写儿童手机远程监控App之vue3用 AI Agent生成菜单
前端
cidy_982 小时前
Git Pull 代码冲突后完整回退教程
前端
JING小白2 小时前
Day 1 重学Vue:响应式系统的“底层逻辑”变更,Vue2旧时代的终结与Vue3新时代的开启
前端·vue.js
张就是我1065922 小时前
一个 ZIP 文件,把 webshell 写到了不该在的地方
前端
张就是我1065922 小时前
SPIP 的一个漏洞:你以为过滤了,其实没过滤干净
前端
一tiao咸鱼2 小时前
我用 Claude 做了一个 AI 面试刷题系统,支持 DeepSeek / 阿里 / GPT 帮你打分
前端
掘金一周3 小时前
对车完全小白,不知买油买电还是买混动,求建议| 沸点周刊 7.2
前端·人工智能·后端