抛开过时的教程,基于 Vite + React 18,手把手带你从零写出第一个组件。不讲虚的,全是干货!
引言:React 到底是个啥?
很多同学觉得 React 很难,学完就忘。其实,React 的核心思想非常简单,就三件事:
- 组件化:把页面像乐高积木一样,拆分成一个个独立的小块(组件)。
- JSX:允许你在 JavaScript 里写类似 HTML 的代码,所见即所得。
- 状态驱动:数据变了,界面自动变。你不需要手动去操作 DOM(比如
document.getElementById)。
只要搞懂这三点,你就入门了。废话少说,咱们直接开干!
第一步:环境搭建——告别黑盒,拥抱 Vite
很多新手被卡在第一步:环境配置。以前我们用 create-react-app (CRA),但现在 2025 年了,那个工具太慢了。
我们用新一代构建工具 Vite,快如闪电,3 分钟搞定环境。
打开你的终端,依次输入以下命令:
# 1. 使用 Vite 脚手架初始化项目
# 这里我们选择 React + TypeScript 模板 (推荐,大型项目更稳)
npm create vite@latest FullStackReactDemo -- --template react-ts
# 2. 进入项目文件夹
cd FullStackReactDemo
# 3. 安装依赖 (这就相当于给汽车加油)
npm i
# 4. 启动开发服务器 (点火!)
npm run dev
终端里会出现一行绿色的字:
➜ Local: http://localhost:5173/
打开浏览器,访问这个地址。如果看到 Vite 的 Logo 和旋转的 React 徽标,恭喜你,环境 OK 了!
第二步:Hello World——第一个 React 组件
现在,我们来创造点东西。
打开 src/App.tsx 文件。删掉里面默认的示例代码,我们写一个最简单的计数器。
修改 src/App.tsx:
// 引入 React 核心的 useState Hook
import { useState } from 'react';
// 定义我们的根组件
function App() {
// 1. 定义状态:count 是变量,setCount 是修改它的函数
// 初始值为 0
const [count, setCount] = useState(0);
// 2. 返回 JSX 视图
return (
<div style={{ textAlign: 'center', marginTop: '60px' }}>
<h1>你好,React 18!</h1>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
</div>
);
}
export default App;
代码解析:
useState(0):这是 React 的一个 Hook(钩子)。它帮我们创建了一个“状态”变量 count。因为 React 要监控数据变化,所以不能像普通 JS 那样直接 let count = 0,必须用 setCount 来修改。
- JSX:你看,我们在 return 里写了 HTML 标签。这就是 JSX。注意,
class 要写成 className,因为这是 JavaScript 对象。
{count}:花括号里可以写任何 JavaScript 表达式。这里把变量 count 的值渲染到了页面上。
保存文件,浏览器会自动热更新。点一下按钮,数字是不是加 1 了?恭喜你,你已经掌握了 React 的核心:状态驱动视图!
第三步:实战 TodoList——组件化思维
光一个按钮太简单了。我们来搞个稍微复杂点的:待办事项列表 (Todo List)。这是检验前端入门的“黄金标准”。
我们要把页面拆成三个组件:
- App:根组件,负责管理所有数据。
- TodoInput:输入框组件,负责添加任务。
- TodoItem:单个任务项组件,负责显示和切换完成状态。
1. 定义数据结构 (TypeScript 的优势)
先在 src/App.tsx 顶部定义一下“待办事项”长什么样:
// 定义 Todo 的类型
export type Todo = {
id: number; // 唯一ID
text: string; // 任务内容
done: boolean; // 是否完成
};
2. 核心逻辑 (App.tsx)
修改 src/App.tsx 的内容:
import { useState } from 'react';
import TodoInput from './components/TodoInput';
import TodoList from './components/TodoList';
import type { Todo } from './App';
function App() {
// 1. 状态:维护一个 todo 列表
const [list, setList] = useState<Todo[]>([]);
// 2. 逻辑:添加任务
const addTodo = (text: string) => {
const newItem: Todo = {
id: Date.now(), // 简单粗暴用时间戳做ID
text,
done: false
};
// 不要直接修改 list,而是返回一个新数组
setList([...list, newItem]);
};
// 3. 逻辑:切换任务完成状态
const toggleTodo = (id: number) => {
setList(
list.map((todo) =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)
);
};
return (
<div style={{ maxWidth: '400px', margin: '0 auto', padding: '20px' }}>
<h2>极简 Todo </h2>
{/* 2. 子组件:输入框,通过 onAdd 传递添加方法 */}
<TodoInput onAdd={addTodo} />
{/* 3. 子组件:列表,传递 list 数据和 toggle 方法 */}
<TodoList todos={list} onToggle={toggleTodo} />
</div>
);
}
export default App;
在 src 目录下新建 components 文件夹,创建 TodoInput.tsx:
import { useState, KeyboardEvent } from 'react';
// 定义接收的属性类型
type Props = {
onAdd: (text: string) => void;
};
export default function TodoInput({ onAdd }: Props) {
const [val, setVal] = useState('');
// 监听键盘事件,按回车添加
const handleKey = (e: KeyboardEvent) => {
if (e.key === 'Enter' && val.trim()) {
onAdd(val.trim());
setVal('');
}
};
return (
<input
type="text"
placeholder="按回车添加任务..."
value={val}
onChange={(e) => setVal(e.target.value)}
onKeyDown={handleKey}
style={{
width: '100%',
padding: '10px',
marginBottom: '10px',
border: '1px solid #ddd',
borderRadius: '4px'
}}
/>
);
}
4. 拆分组件:TodoItem 和 TodoList
src/components/TodoItem.tsx (单个任务项):
import type { Todo } from '../App';
type Props = {
todo: Todo;
onToggle: (id: number) => void;
};
export default function TodoItem({ todo, onToggle }: Props) {
return (
<li
onClick={() => onToggle(todo.id)}
style={{
cursor: 'pointer',
padding: '8px 0',
textDecoration: todo.done ? 'line-through' : 'none',
color: todo.done ? '#888' : '#222',
listStyle: 'none'
}}
>
{todo.text}
</li>
);
}
src/components/TodoList.tsx (列表容器):
import type { Todo } from '../App';
import TodoItem from './TodoItem';
type Props = {
todos: Todo[];
onToggle: (id: number) => void;
};
export default function TodoList({ todos, onToggle }: Props) {
if (todos.length === 0) return <p>暂无任务,快去添加一个吧~</p>;
return (
<ul style={{ padding: 0, listStyle: 'none' }}>
{todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} onToggle={onToggle} />
))}
</ul>
);
}
结语:你已经入门了!
停一下手里的活儿,看看浏览器。
输入文字,按回车,任务出现了!点一下任务,它划掉了!这可是 Facebook 开源的 React 核心思想在你手里的完美体现!
咱们回顾一下这 30 分钟干了啥:
- 搭环境:用 Vite 3 分钟跑通开发环境,掌握了现代 前端工程化 的起点。
- 写组件:学会了函数式组件和 JSX 语法。
- 管状态:用
useState 实现了数据驱动视图。
- 组件通信:父组件 (
App) 通过 props 把数据和方法传给子组件 (TodoInput, TodoItem)。
接下来的学习建议:
- 学
useEffect:处理副作用(比如页面加载时去请求数据)。
- 学路由 (React Router):实现页面跳转。
- 学状态管理 (Redux Toolkit / Zustand):当组件越来越多,数据传递很麻烦时用。
React 并不难,难的是迈出第一步。现在,你已经迈出去了。如果想深入学习更多实用的 技术文档 与实战项目,欢迎来 云栈社区 交流探讨。