Appearance
React 框架基础
简介
React 是用于构建用户界面的 JavaScript 库,核心思想是组件化、声明式 UI、单向数据流。
环境搭建
sh
npm create vite@latest my-app -- --template react
npm create vite@latest my-app -- --template react-ts # TypeScript
cd my-app
npm install
npm run devJSX
jsx
// JSX 是 JavaScript 的语法扩展,允许在 JS 中写 HTML-like 代码
const element = <h1>Hello, React!</h1>
// 嵌入表达式
const name = "React"
const element = <h1>Hello, {name}</h1>
// 属性
const img = <img src={url} alt="描述" />
// 条件渲染
{isLoggedIn && <UserGreeting />}
{isLoggedIn ? <LogoutButton /> : <LoginButton />}
// 列表渲染
{items.map(item => <li key={item.id}>{item.name}</li>)}组件
函数组件(推荐)
jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>
}
// 箭头函数
const Welcome = ({ name }) => <h1>Hello, {name}</h1>组件组合
jsx
function App() {
return (
<div>
<Welcome name="Alice" />
<Welcome name="Bob" />
</div>
)
}Props
jsx
function User({ name, age, isAdmin, children }) {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
{isAdmin && <span>管理员</span>}
{children}
</div>
)
}
// 使用
<User name="Alice" age={30} isAdmin>
<p>额外的子元素</p>
</User>State
jsx
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(prev => prev + 1)}>+1(函数式更新)</button>
</div>
)
}useState 注意事项
jsx
// 对象 state(需展开合并)
const [user, setUser] = useState({ name: '', age: 0 })
setUser({ ...user, name: 'Alice' })
// 数组 state
const [items, setItems] = useState([1, 2, 3])
setItems([...items, 4]) // 追加
setItems(items.filter(i => i > 1)) // 删除
setItems(items.map(i => i * 2)) // 更新useEffect
jsx
import { useState, useEffect } from 'react'
function DataFetcher() {
const [data, setData] = useState(null)
// 组件挂载时执行一次
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData)
}, []) // 空依赖:仅在挂载时执行
// 依赖变化时执行
useEffect(() => {
document.title = `点击了 ${count} 次`
}, [count])
// 清理函数(组件卸载或依赖变化前执行)
useEffect(() => {
const timer = setInterval(() => setCount(c => c + 1), 1000)
return () => clearInterval(timer)
}, [])
return <div>{JSON.stringify(data)}</div>
}事件处理
jsx
function Form() {
const [value, setValue] = useState('')
const handleSubmit = (e) => {
e.preventDefault() // 阻止默认行为
console.log(value)
}
return (
<form onSubmit={handleSubmit}>
<input value={value} onChange={e => setValue(e.target.value)} />
<button type="submit">提交</button>
</form>
)
}受控组件
jsx
function LoginForm() {
const [form, setForm] = useState({ email: '', password: '' })
const handleChange = (e) => {
setForm({ ...form, [e.target.name]: e.target.value })
}
return (
<form>
<input name="email" value={form.email} onChange={handleChange} />
<input name="password" type="password" value={form.password} onChange={handleChange} />
</form>
)
}useRef
jsx
import { useRef } from 'react'
function TextInput() {
const inputRef = useRef(null)
const focus = () => {
inputRef.current.focus()
}
return (
<>
<input ref={inputRef} />
<button onClick={focus}>聚焦输入框</button>
</>
)
}useContext
jsx
import { createContext, useContext } from 'react'
// 创建 Context
const ThemeContext = createContext('light')
// 提供者
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
)
}
// 消费者
function Toolbar() {
const theme = useContext(ThemeContext)
return <div className={theme}>当前主题: {theme}</div>
}useReducer
jsx
import { useReducer } from 'react'
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count - 1 }
case 'reset':
return { count: 0 }
default:
throw new Error('未知 action')
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 })
return (
<div>
<p>计数: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
<button onClick={() => dispatch({ type: 'reset' })}>重置</button>
</div>
)
}自定义 Hook
jsx
import { useState, useEffect } from 'react'
// 自定义 Hook(必须以 use 开头)
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth)
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return width
}
// 使用
function MyComponent() {
const width = useWindowWidth()
return <p>窗口宽度: {width}px</p>
}常用自定义 Hook 示例
jsx
// 防抖
function useDebounce(value, delay = 300) {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay)
return () => clearTimeout(timer)
}, [value, delay])
return debouncedValue
}
// 异步请求
function useFetch(url) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
setLoading(true)
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false))
}, [url])
return { data, loading, error }
}React 路由
sh
npm install react-router-domjsx
import { BrowserRouter, Routes, Route, Link, useParams, useNavigate } from 'react-router-dom'
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/users/1">用户</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/:id" element={<UserDetail />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
)
}
function UserDetail() {
const { id } = useParams()
const navigate = useNavigate()
return (
<div>
<p>用户 ID: {id}</p>
<button onClick={() => navigate('/')}>返回首页</button>
</div>
)
}React 与 TypeScript
tsx
interface UserProps {
name: string
age: number
avatar?: string // 可选
onUpdate: (id: number) => void
}
const User: React.FC<UserProps> = ({ name, age, onUpdate }) => {
return (
<div>
<p>{name} ({age})</p>
<button onClick={() => onUpdate(1)}>更新</button>
</div>
)
}
// 泛型 Hook
function useLocalStorage<T>(key: string, initial: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key)
return stored ? JSON.parse(stored) : initial
})
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value))
}, [key, value])
return [value, setValue] as const
}常见模式
条件渲染
jsx
{loading && <Spinner />}
{error ? <Error msg={error} /> : <Data />}列表与 key
jsx
{items.map(item => <ListItem key={item.id} item={item} />)}提升状态
jsx
// 将共享 state 提升到共同的父组件
function Parent() {
const [value, setValue] = useState('')
return (
<>
<Input value={value} onChange={setValue} />
<Display value={value} />
</>
)
}React 开发者工具
sh
# Chrome 扩展
React Developer Tools
# 浏览器中可查看:
# - 组件树与 props/state
# - 性能分析(Profiler)
# - 组件高亮常用库
| 库 | 用途 |
|---|---|
react-router-dom | 路由 |
axios | HTTP 请求 |
react-query / @tanstack/react-query | 服务端状态管理 |
zustand | 轻量状态管理 |
redux toolkit | 大型应用状态管理 |
react-hook-form | 表单处理 |
zod / yup | 表单验证 |
antd / element-plus | UI 组件库 |
tailwindcss | 原子化 CSS |
date-fns / dayjs | 日期处理 |
react-i18next | 国际化 |
recharts | 图表 |