07. React核心原理和生态
📋 目录
React架构深度解析
React 的核心思想是将UI视为函数的纯粹输出,通过声明式编程和组件化思想构建用户界面。
React核心概念
React设计理念
概念 | 说明 | 特点 | 优势 |
---|---|---|---|
UI = f(state) | UI是状态的纯函数 | 声明式编程 | 可预测、易调试 |
组件化 | 将UI拆分为独立组件 | 可复用、可组合 | 提高开发效率 |
Virtual DOM | 虚拟DOM抽象层 | 内存中的JS对象 | 性能优化、跨平台 |
单向数据流 | 数据从上到下流动 | 数据流清晰 | 易于理解和维护 |
核心概念示例
// 1. 函数式组件的本质
function MyComponent(props) {
// 这就是一个纯函数:相同输入 -> 相同输出
return <div>{props.children}</div>;
}
// 2. JSX编译过程
// JSX语法糖
const element = <h1 className="greeting">Hello, world!</h1>;
// 编译后的真实代码
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, world!'
);
// 3. Virtual DOM结构
const virtualDOM = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
},
key: null,
ref: null
};
React核心特性
- 声明式:描述UI应该是什么样子,而不是如何操作DOM
- 组件化:将复杂UI拆分为简单、可复用的组件
- 一次学习,随处编写:React Native、React Server Components等
- 虚拟DOM:提供性能优化和跨平台能力
React渲染流程
⚠️
理解React的渲染流程对于编写高性能的React应用至关重要。React的渲染过程分为三个主要阶段。
React渲染三阶段
阶段 | 特性 | 主要工作 | 可中断性 |
---|---|---|---|
Render Phase | 纯函数,无副作用 | 构建Fiber树、执行组件函数、Diffing | ✅ 可中断 |
Commit Phase | 操作DOM | DOM更新、生命周期、useLayoutEffect | ❌ 不可中断 |
Passive Effects | 异步执行 | useEffect回调 | ✅ 可延迟 |
更新触发方式
触发方式 | 说明 | 示例 |
---|---|---|
首次渲染 | 组件首次挂载 | ReactDOM.render() |
状态更新 | setState/useState | setCount(count + 1) |
强制更新 | 跳过shouldComponentUpdate | forceUpdate() |
父组件更新 | 父组件重新渲染 | props变化 |
渲染流程示例
// 渲染流程示例
function Counter() {
console.log("1. 组件函数执行"); // Render阶段
const [count, setCount] = useState(0);
useLayoutEffect(() => {
console.log("3. useLayoutEffect执行"); // Commit阶段
});
useEffect(() => {
console.log("4. useEffect执行"); // Passive Effects阶段
});
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>
增加
</button>
</div>
);
}
// 执行顺序:
// 1. 组件函数执行 (Render)
// 2. DOM更新 (Commit)
// 3. useLayoutEffect执行 (Commit)
// 4. useEffect执行 (Passive Effects)
Fiber架构和调度机制
Fiber架构是React 16引入的重大架构升级,它使React能够实现可中断的渲染和优先级调度。
Fiber架构优势
特性 | React 15 (Stack) | React 16+ (Fiber) | 优势 |
---|---|---|---|
渲染方式 | 同步递归 | 可中断渲染 | 避免长时间阻塞 |
优先级调度 | 无 | 支持优先级 | 重要更新优先处理 |
错误边界 | 有限支持 | 完整支持 | 更好的错误处理 |
并发特性 | 不支持 | 支持 | Suspense、并发渲染 |
Fiber节点结构
// Fiber节点的核心结构
const FiberNode = {
// 节点信息
type: null, // 组件类型
key: null, // React key
stateNode: null, // DOM节点或组件实例
// 树结构
return: null, // 父节点
child: null, // 第一个子节点
sibling: null, // 兄弟节点
// 状态管理
pendingProps: null, // 新props
memoizedProps: null, // 旧props
memoizedState: null, // 旧state
updateQueue: null, // 更新队列
// 调度相关
lanes: NoLanes, // 优先级
flags: NoFlags, // 副作用标记
alternate: null // 双缓存指针
};
时间切片机制
// 时间切片示例
function workLoopConcurrent() {
// 在有剩余时间时继续工作
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}
function shouldYield() {
// 检查是否应该让出控制权
return getCurrentTime() >= deadline;
}
#### 优先级调度
| 优先级 | 值 | 使用场景 | 示例 |
|--------|----|---------|----- |
| **Immediate** | 1 | 立即执行 | 用户输入、focus事件 |
| **UserBlocking** | 2 | 用户阻塞 | 点击、滚动、拖拽 |
| **Normal** | 3 | 正常优先级 | 网络请求、数据更新 |
| **Low** | 4 | 低优先级 | 分析统计、日志 |
| **Idle** | 5 | 空闲时执行 | 预加载、缓存清理 |
## Hooks原理和最佳实践
### Hooks实现原理
<Callout type="warning">
Hooks的实现基于链表结构,这就是为什么Hooks必须在组件顶层调用,不能在条件语句中使用。
</Callout>
#### Hooks规则
| 规则 | 说明 | 原因 | 正确示例 |
|------|------|------|----------|
| **顶层调用** | 只在函数顶层调用Hooks | 保证调用顺序一致 | `const [state, setState] = useState()` |
| **React函数中调用** | 只在React函数组件或自定义Hook中调用 | 确保在React上下文中 | 函数组件、自定义Hook |
| **不在循环/条件中** | 不在循环、条件或嵌套函数中调用 | 保证每次渲染调用顺序相同 | 避免`if (condition) useState()` |
#### Hooks实现原理
```javascript
// useState简化实现
let hookIndex = 0;
let hooks = [];
function useState(initialValue) {
const currentIndex = hookIndex;
// 初始化hook
if (hooks[currentIndex] === undefined) {
hooks[currentIndex] = {
state: initialValue,
queue: []
};
}
const hook = hooks[currentIndex];
// 处理更新队列
hook.queue.forEach(action => {
hook.state = typeof action === 'function' ? action(hook.state) : action;
});
hook.queue = [];
const setState = (action) => {
hook.queue.push(action);
rerender(); // 触发重新渲染
};
hookIndex++;
return [hook.state, setState];
}
// useEffect简化实现
function useEffect(callback, deps) {
const currentIndex = hookIndex;
if (hooks[currentIndex] === undefined) {
hooks[currentIndex] = { callback, deps, cleanup: null };
hooks[currentIndex].cleanup = callback(); // 首次执行
} else {
const hook = hooks[currentIndex];
// 检查依赖变化
const depsChanged = deps ?
!deps.every((dep, i) => dep === hook.deps[i]) : true;
if (depsChanged) {
if (hook.cleanup) hook.cleanup(); // 清理
hook.cleanup = callback(); // 执行新副作用
hook.deps = deps;
}
}
hookIndex++;
}
常用Hooks对比
Hook | 用途 | 返回值 | 使用场景 |
---|---|---|---|
useState | 状态管理 | [state, setState] | 组件内部状态 |
useEffect | 副作用处理 | undefined | 数据获取、订阅、DOM操作 |
useContext | 上下文消费 | contextValue | 跨组件数据传递 |
useReducer | 复杂状态管理 | [state, dispatch] | 复杂状态逻辑 |
useMemo | 值缓存 | memoizedValue | 昂贵计算优化 |
useCallback | 函数缓存 | memoizedCallback | 函数引用优化 |
自定义Hooks最佳实践
常用自定义Hooks
Hook名称 | 功能 | 返回值 | 使用场景 |
---|---|---|---|
useLocalStorage | 本地存储同步 | [value, setValue] | 用户偏好设置 |
useDebounce | 防抖处理 | debouncedValue | 搜索输入 |
useAsync | 异步状态管理 | {data, loading, error} | API调用 |
useToggle | 布尔值切换 | [value, toggle] | 开关状态 |
自定义Hooks示例
// 1. useLocalStorage - 本地存储Hook
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
const setValue = useCallback((value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(`Error setting localStorage:`, error);
}
}, [key, storedValue]);
return [storedValue, setValue];
}
// 2. useDebounce - 防抖Hook
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debouncedValue;
}
// 3. useAsync - 异步状态管理Hook
function useAsync(asyncFunction, dependencies = []) {
const [state, setState] = useState({
data: null,
loading: false,
error: null
});
useEffect(() => {
let cancelled = false;
setState({ data: null, loading: true, error: null });
asyncFunction()
.then(data => !cancelled && setState({ data, loading: false, error: null }))
.catch(error => !cancelled && setState({ data: null, loading: false, error }));
return () => { cancelled = true; };
}, dependencies);
return state;
}
自定义Hooks设计原则
- 单一职责:每个Hook只负责一个功能
- 可复用性:设计通用的参数接口
- 错误处理:提供完善的错误处理机制
- 性能优化:使用useMemo和useCallback优化性能
- 清理资源:在useEffect中正确清理副作用
状态管理方案对比
React内置状态管理
useState vs useReducer
特性 | useState | useReducer | 适用场景 |
---|---|---|---|
复杂度 | 简单 | 复杂 | useState适合简单状态,useReducer适合复杂状态逻辑 |
状态更新 | 直接设置 | 通过action | useReducer更适合多种状态变更方式 |
可预测性 | 一般 | 高 | useReducer的状态变更更可预测 |
调试 | 简单 | 容易追踪 | useReducer便于调试状态变更 |
Redux vs Zustand vs Jotai
// 1. Redux + Redux Toolkit
import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
// 2. Zustand
import { create } from 'zustand';
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}));
// 3. Jotai
import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
const doubleCountAtom = atom((get) => get(countAtom) * 2);
function JotaiCounter() {
const [count, setCount] = useAtom(countAtom);
const [doubleCount] = useAtom(doubleCountAtom);
return (
<div>
<span>Count: {count}</span>
<span>Double: {doubleCount}</span>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}
状态管理方案选择指南
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
Redux | 大型复杂应用 | 可预测、调试工具强大 | 样板代码多、学习曲线陡 |
Zustand | 中小型应用 | 轻量、API简单 | 生态相对较小 |
Jotai | 需要细粒度控制 | 原子化、避免不必要重渲染 | 概念相对新颖 |
Context | 简单全局状态 | React内置、无需依赖 | 性能问题、不适合频繁更新 |
React性能优化
组件优化技巧
// 1. React.memo - 组件记忆化
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
console.log('ExpensiveComponent 重新渲染');
return (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}, (prevProps, nextProps) => {
// 自定义比较函数
return prevProps.data.length === nextProps.data.length &&
prevProps.onUpdate === nextProps.onUpdate;
});
// 2. useMemo - 值记忆化
function DataProcessor({ items, filter }) {
const expensiveValue = useMemo(() => {
console.log('计算昂贵的值');
return items
.filter(item => item.category === filter)
.reduce((sum, item) => sum + item.value, 0);
}, [items, filter]);
return <div>总计: {expensiveValue}</div>;
}
// 3. useCallback - 函数记忆化
function ParentComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// 没有useCallback,每次渲染都会创建新函数
const handleClick = useCallback(() => {
console.log('按钮被点击');
}, []); // 空依赖数组,函数永远不会改变
const handleNameChange = useCallback((newName) => {
setName(newName);
}, []); // 使用函数式更新,避免依赖name
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>增加</button>
<ExpensiveChild
onClick={handleClick}
onNameChange={handleNameChange}
/>
</div>
);
}
// 4. 代码分割和懒加载
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>加载中...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
// 5. 虚拟滚动
function VirtualList({ items, itemHeight = 50 }) {
const [scrollTop, setScrollTop] = useState(0);
const containerHeight = 400;
const visibleCount = Math.ceil(containerHeight / itemHeight);
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(startIndex + visibleCount, items.length);
const visibleItems = items.slice(startIndex, endIndex);
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: items.length * itemHeight, position: 'relative' }}>
{visibleItems.map((item, index) => (
<div
key={startIndex + index}
style={{
position: 'absolute',
top: (startIndex + index) * itemHeight,
height: itemHeight,
width: '100%'
}}
>
{item.name}
</div>
))}
</div>
</div>
);
}
React生态工具链
React 拥有丰富的生态系统,包括开发工具、状态管理、路由、测试等各个方面的解决方案。
开发工具
# Create React App - 官方脚手架
npx create-react-app my-app
cd my-app
npm start
# Vite + React - 现代构建工具
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev
# Next.js - 全栈React框架
npx create-next-app@latest my-next-app
cd my-next-app
npm run dev
状态管理工具
// Redux Toolkit - 现代Redux
import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
}
}
});
export const { increment, decrement } = counterSlice.actions;
export const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
// Zustand - 轻量级状态管理
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 }))
}));
路由管理
// React Router v6
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/users/:id" element={<UserProfile />} />
</Routes>
</BrowserRouter>
);
}
UI组件库
// Ant Design
import { Button, Table, Form, Input } from 'antd';
function MyComponent() {
return (
<Form>
<Form.Item label="用户名" name="username">
<Input />
</Form.Item>
<Button type="primary" htmlType="submit">
提交
</Button>
</Form>
);
}
// Material-UI (MUI)
import { Button, TextField, Box } from '@mui/material';
function MyComponent() {
return (
<Box component="form">
<TextField label="用户名" variant="outlined" />
<Button variant="contained" color="primary">
提交
</Button>
</Box>
);
}
测试工具
// Jest + React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import Counter from './Counter';
test('renders counter and increments', () => {
render(<Counter />);
const counter = screen.getByText(/count: 0/i);
expect(counter).toBeInTheDocument();
const button = screen.getByRole('button', { name: /increment/i });
fireEvent.click(button);
expect(screen.getByText(/count: 1/i)).toBeInTheDocument();
});
// Cypress E2E测试
describe('Counter App', () => {
it('should increment counter', () => {
cy.visit('/');
cy.contains('Count: 0');
cy.get('[data-testid="increment-button"]').click();
cy.contains('Count: 1');
});
});
理解React的核心原理和Fiber架构是成为React高级开发者的关键。这些知识将帮助你编写更高效、更可维护的React应用。
📚 参考学习资料
📖 官方文档
- React 官方文档 - React官方学习资源
- React API 参考 - 完整API文档
- React DevTools - 官方调试工具
🎓 优质教程
- React 官方教程 - 官方入门教程
- React Patterns - React设计模式
- Epic React - Kent C. Dodds的React课程
🛠️ 实践项目
- React 项目集合 - 官方脚手架
- React 示例项目 - 官方示例
- Awesome React - React资源集合
🔧 开发工具
- Create React App - 官方脚手架
- Vite React 模板 - 现代构建工具
- React DevTools - 浏览器扩展
📝 深入阅读
- React Fiber 架构 - Fiber原理解析
- React 源码解析 - 源码学习指南
- React 性能优化 - Google Web.dev React指南
💡 学习建议:建议从官方文档开始,通过Create React App创建项目实践,然后深入学习Hooks和性能优化,最后了解Fiber架构原理。
Last updated on