好的,这是一个详细的 React 入门教程,希望能帮助你快速上手!🚀
React 是一个用于构建用户界面(UI)的 JavaScript 库。它由 Facebook 开发并维护,被广泛用于构建单页面应用程序(SPA)和移动应用。
📚 1. 学习 React 前的准备
在开始学习 React 之前,你需要对以下技术有一定的了解:
- HTML (超文本标记语言):了解基本的 HTML 结构和标签。
- CSS (层叠样式表):了解如何使用 CSS 来设置页面样式。
- JavaScript (JS):这是最重要的。你需要掌握 JavaScript 的基础知识,包括:
- 变量、数据类型、操作符
- 函数(特别是箭头函数
=>
) - 数组和对象(包括解构赋值、扩展运算符
...
) - ES6+ 模块 (
import
/export
) this
关键字 (虽然在函数组件中不常用,但理解其基本概念有益)- 异步编程 (Promises, async/await) - 在后续学习中会用到
🛠️ 2. 搭建 React 开发环境
最快上手 React 的方式是使用官方推荐的脚手架工具 Create React App
。
安装 Node.js 和 npm (或 yarn):
- React 开发依赖 Node.js 环境。访问 Node.js 官网 下载并安装最新 LTS (长期支持) 版本。
npm
(Node Package Manager) 会随 Node.js 一起安装。你也可以选择使用yarn
作为包管理工具。
创建你的第一个 React 应用: 打开你的终端或命令行工具,运行以下命令:
npx create-react-app my-react-app
这里
my-react-app
是你的项目名称,你可以替换成任何你喜欢的名字。npx
是一个 npm 包运行器,它会下载最新版本的create-react-app
并执行它。进入项目目录并启动开发服务器:
cd my-react-app npm start
或者,如果你使用 yarn:
cd my-react-app yarn start
这会启动一个本地开发服务器(通常在
http://localhost:3000
),并在你的浏览器中自动打开应用。当你修改代码并保存时,页面会自动刷新。
📂 3. 了解项目结构
create-react-app
会生成一个标准的项目结构:
my-react-app/
├── node_modules/ # 项目依赖的第三方库
├── public/ # 存放静态资源
│ ├── index.html # 应用的 HTML 入口文件
│ └── ... # 其他如 favicon.ico, manifest.json 等
├── src/ # 主要的源代码目录
│ ├── App.css # App 组件的样式
│ ├── App.js # 主要的 App 组件
│ ├── App.test.js # App 组件的测试文件
│ ├── index.css # 全局样式
│ ├── index.js # JavaScript 的入口文件,将 App 组件渲染到 HTML 中
│ ├── logo.svg # 示例图片
│ └── ... # 其他辅助文件
├── .gitignore # Git 忽略文件配置
├── package.json # 项目元数据和依赖项列表
├── package-lock.json # (或 yarn.lock) 锁定依赖版本
└── README.md # 项目说明文件
public/index.html
:这是你的单页面应用的唯一 HTML 文件。React 会将你的组件渲染到这个文件中的某个 DOM 元素(通常是一个id="root"
的div
)。src/index.js
:这是 JavaScript 的入口。它使用ReactDOM.render()
方法将你的主组件(通常是App
组件)挂载到public/index.html
中的div#root
元素上。src/App.js
:这是应用的根组件。你将从这里开始构建你的用户界面。
✨ 4. React核心概念
a. JSX (JavaScript XML)
JSX 是一种 JavaScript 的语法扩展,它允许你在 JavaScript 代码中编写类似 HTML 的结构。浏览器并不直接支持 JSX,它需要通过 Babel 这样的转译器转换为普通的 JavaScript 对象。
示例:
const name = "React 学习者";
const element = <h1>你好, {name}!</h1>; // JSX 表达式
// 上面的 JSX 实际上会被 Babel 转译成类似这样的 JavaScript:
// const element = React.createElement('h1', null, '你好, ', name, '!');
在 JSX 中:
- 你可以嵌入 JavaScript 表达式,用花括号
{}
包裹。 - HTML 属性名需要使用驼峰命名法(例如
className
而不是class
,onClick
而不是onclick
)。 - 所有标签必须闭合(例如
<img />
或<div></div>
)。
b. 组件 (Components)
组件是 React 的核心。它们是独立且可复用的代码块,负责渲染 UI 的一部分。React 主要有两种类型的组件:
函数组件 (Functional Components):这是目前推荐的方式。它们是简单的 JavaScript 函数,接收一个
props
对象作为参数,并返回一个 React 元素(通常是 JSX)。// src/components/Welcome.js import React from 'react'; function Welcome(props) { return <h1>你好, {props.name}!</h1>; } export default Welcome;
使用组件:
// src/App.js import React from 'react'; import Welcome from './components/Welcome'; // 假设你创建了components目录 function App() { return ( <div> <Welcome name="张三" /> <Welcome name="李四" /> </div> ); } export default App;
类组件 (Class Components):这是老版本 React 中常用的方式,基于 ES6 的
class
。它们需要继承React.Component
并实现一个render()
方法。// src/components/Greeting.js import React from 'react'; class Greeting extends React.Component { render() { return <h1>你好, {this.props.name}! (来自类组件)</h1>; } } export default Greeting;
建议:对于新项目,优先使用函数组件和 Hooks。
c. Props (属性)
Props
(properties 的缩写) 是组件的配置信息。它们是从父组件传递给子组件的数据。Props 是只读的,子组件不能直接修改接收到的 props。
示例见上面的 Welcome
组件,name
就是一个 prop。
d. State (状态)
State
是组件内部私有的数据,它可以随时间变化(例如用户交互、网络响应等)。当组件的 state 改变时,React 会自动重新渲染该组件。
在函数组件中,我们使用 useState
Hook 来管理 state。
// src/components/Counter.js
import React, { useState } from 'react'; // 引入 useState Hook
function Counter() {
// 声明一个名为 count 的 state 变量,初始值为 0
// setCount 是一个用来更新 count 的函数
const [count, setCount] = useState(0);
return (
<div>
<p>你点击了 {count} 次</p>
<button onClick={() => setCount(count + 1)}>
点我增加
</button>
<button onClick={() => setCount(count > 0 ? count - 1 : 0)}>
点我减少
</button>
</div>
);
}
export default Counter;
关键点:
useState
返回一个数组:[currentStateValue, functionToUpdateState]
。- 通过调用
setCount(newValue)
来更新count
的值,这会触发组件的重新渲染。 - 永远不要直接修改 state (例如
count = count + 1;
),总是使用set
函数。
e. 事件处理 (Event Handling)
React 元素的事件处理和 DOM 元素的事件处理非常相似,但有一些语法差异:
- React 事件的命名采用驼峰式,而不是纯小写 (例如
onClick
而不是onclick
)。 - 通过 JSX,你传递一个函数作为事件处理程序,而不是一个字符串。
示例见上面的 Counter
组件中的 onClick
事件。
f. 条件渲染 (Conditional Rendering)
你可以根据应用的 state 或 props 来决定渲染哪些组件或元素。常用的方法有:
if
语句- 逻辑与运算符
&&
- 三元运算符
condition ? trueValue : falseValue
// src/components/LoginControl.js
import React, { useState } from 'react';
function UserGreeting(props) {
return <h1>欢迎回来!</h1>;
}
function GuestGreeting(props) {
return <h1>请登录.</h1>;
}
function LoginButton(props) {
return <button onClick={props.onClick}>登录</button>;
}
function LogoutButton(props) {
return <button onClick={props.onClick}>退出</button>;
}
function LoginControl() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const handleLoginClick = () => {
setIsLoggedIn(true);
};
const handleLogoutClick = () => {
setIsLoggedIn(false);
};
let button;
let greeting;
if (isLoggedIn) {
button = <LogoutButton onClick={handleLogoutClick} />;
greeting = <UserGreeting />;
} else {
button = <LoginButton onClick={handleLoginClick} />;
greeting = <GuestGreeting />;
}
return (
<div>
{greeting}
{button}
{/* 使用三元运算符的另一种方式 */}
{/* {isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
{isLoggedIn
? <LogoutButton onClick={handleLogoutClick} />
: <LoginButton onClick={handleLoginClick} />} */}
</div>
);
}
export default LoginControl;
g. 列表和 Keys (Lists and Keys)
你可以使用 JavaScript 的 map()
方法来将数组渲染为元素列表。当渲染列表时,React 需要一个特殊的 key
prop 来帮助识别哪些列表项被更改、添加或删除。key
应该是唯一的字符串,通常使用数据项中的 ID。
// src/components/NumberList.js
import React from 'react';
function ListItem(props) {
// 正确!这里不需要指定 key:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 正确!key 应该在数组的上下文中被指定
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
export default NumberList;
// 在 App.js 中使用:
// import NumberList from './components/NumberList';
// const numbers = [1, 2, 3, 4, 5];
// <NumberList numbers={numbers} />
重要提示:key
只需要在兄弟节点中唯一,不需要全局唯一。不要使用数组的索引作为 key
,除非列表是静态的且不会重新排序。
🚀 5. 第一个动手实践:待办事项列表 (Todo List)
让我们结合以上概念,尝试构建一个简单的待办事项列表。
创建
TodoItem.js
组件 (src/components/TodoItem.js
)import React from 'react'; function TodoItem({ todo, index, toggleTodo, removeTodo }) { return ( <li style={{ textDecoration: todo.isCompleted ? 'line-through' : '' }}> {todo.text} <div> <button onClick={() => toggleTodo(index)}> {todo.isCompleted ? '撤销' : '完成'} </button> <button onClick={() => removeTodo(index)} style={{ marginLeft: '10px' }}> 删除 </button> </div> </li> ); } export default TodoItem;
创建
TodoList.js
组件 (src/components/TodoList.js
)import React, { useState } from 'react'; import TodoItem from './TodoItem'; function TodoList() { const [todos, setTodos] = useState([ { text: '学习 React', isCompleted: false }, { text: '构建一个 Todo 应用', isCompleted: false }, ]); const [inputValue, setInputValue] = useState(''); const addTodo = (text) => { if (!text.trim()) return; // 不添加空内容 const newTodos = [...todos, { text, isCompleted: false }]; setTodos(newTodos); }; const toggleTodo = (index) => { const newTodos = [...todos]; newTodos[index].isCompleted = !newTodos[index].isCompleted; setTodos(newTodos); }; const removeTodo = (index) => { const newTodos = [...todos]; newTodos.splice(index, 1); setTodos(newTodos); }; const handleSubmit = (e) => { e.preventDefault(); // 阻止表单默认提交行为 addTodo(inputValue); setInputValue(''); // 清空输入框 }; return ( <div className="todo-list"> <h1>我的待办事项</h1> <form onSubmit={handleSubmit}> <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} placeholder="添加新的待办..." /> <button type="submit">添加</button> </form> <ul> {todos.map((todo, index) => ( <TodoItem key={index} // 注意:这里用 index 做 key 仅为演示,实际项目中如有唯一 ID 更好 index={index} todo={todo} toggleTodo={toggleTodo} removeTodo={removeTodo} /> ))} </ul> </div> ); } export default TodoList;
你可能需要一些简单的 CSS 来美化它。在
src/App.css
(或新建一个TodoList.css
并导入) 添加:/* src/App.css 或 src/TodoList.css */ .todo-list { font-family: sans-serif; max-width: 500px; margin: 50px auto; padding: 20px; border: 1px solid #eee; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .todo-list h1 { text-align: center; color: #333; } .todo-list form { display: flex; margin-bottom: 20px; } .todo-list input[type="text"] { flex-grow: 1; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; } .todo-list button { padding: 10px 15px; margin-left: 10px; background-color: #5cb85c; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } .todo-list button:hover { background-color: #4cae4c; } .todo-list ul { list-style-type: none; padding: 0; } .todo-list li { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid #eee; } .todo-list li:last-child { border-bottom: none; } .todo-list li button { background-color: #d9534f; font-size: 12px; padding: 5px 10px; } .todo-list li button:first-of-type { /* 针对 "完成/撤销" 按钮 */ background-color: #f0ad4e; }
在
src/App.js
中使用TodoList
组件import React from 'react'; import './App.css'; // 确保引入了样式 import TodoList from './components/TodoList'; // 确保路径正确 function App() { return ( <div className="App"> <TodoList /> </div> ); } export default App;
现在,你的浏览器应该显示一个可以添加、完成和删除待办事项的列表了!
💡 6. React Hooks 简介
Hooks 是 React 16.8 版本引入的新特性,它允许你在不编写 class 的情况下使用 state 以及其他的 React 特性。
useState
:如上所述,用于在函数组件中添加 state。useEffect
:用于处理副作用(side effects),例如数据获取、订阅或手动更改 DOM。它类似于类组件中的componentDidMount
、componentDidUpdate
和componentWillUnmount
的组合。import React, { useState, useEffect } from 'react'; function Timer() { const [seconds, setSeconds] = useState(0); useEffect(() => { // 这个函数会在组件挂载后以及每次更新后执行 const intervalId = setInterval(() => { setSeconds(prevSeconds => prevSeconds + 1); }, 1000); // 清理函数:这个函数会在组件卸载前执行 return () => clearInterval(intervalId); }, []); // 空数组 [] 表示这个 effect 只在组件挂载和卸载时运行一次 return ( <div> 计时器: {seconds} 秒 </div> ); } export default Timer;
useEffect
的依赖项数组:[]
(空数组):Effect 只在挂载时运行一次,清理函数在卸载时运行。[var1, var2]
:Effect 在挂载时运行,并且在var1
或var2
改变后的每次渲染时运行。- 不传第二个参数(不推荐):Effect 在每次渲染后都会运行。
还有许多其他的 Hooks,如 useContext
, useReducer
, useCallback
, useMemo
, useRef
等,你可以在后续学习中逐步掌握。
🚀 7. 接下来学什么?
当你掌握了以上基础知识后,可以继续学习:
- 表单处理 (Forms):更深入地学习受控组件和非受控组件。
- 组件生命周期 (Lifecycle Methods):虽然 Hooks 简化了许多,但了解类组件的生命周期方法对理解 React 的工作方式仍有帮助。
- 路由 (Routing):使用
React Router
来构建多页面的单页面应用。 - 状态管理 (State Management):
- Context API:React 内置的,用于在组件树中共享状态,避免 props drilling (逐层传递 props)。
- Redux, Zustand, MobX:更强大的第三方状态管理库,适用于大型复杂应用。
- 数据获取 (Data Fetching):如何从后端 API 获取数据并展示(例如使用
fetch
API 或axios
库,并结合useEffect
)。 - 组件库 (Component Libraries):例如 Material-UI, Ant Design, Chakra UI 等,它们提供了预制的 UI 组件,可以加速开发。
- 测试 (Testing):使用 Jest, React Testing Library 等工具为你的组件编写测试。
- 构建和部署 (Build and Deployment):学习如何将你的 React 应用打包并部署到服务器或静态网站托管平台。
- TypeScript 与 React:为你的 React 项目添加静态类型检查。
- 性能优化 (Performance Optimization):
React.memo
,useCallback
,useMemo
, 代码分割等。
💖 8. 学习建议
- 多动手实践:理论学习很重要,但通过实际编写代码来巩固知识更重要。
- 阅读官方文档:React 的官方文档非常出色,是学习的最佳资源。
- 参与社区:在 Stack Overflow, Reddit (r/reactjs), DEV Community 等社区提问和交流。
- 从小项目开始:逐步增加项目的复杂度。
- 保持耐心:学习曲线可能有些陡峭,但坚持下去就会有收获。
祝你 React 学习之旅愉快!如果你在学习过程中遇到任何问题,随时可以提出。