前端设计模式详解
前端设计模式是针对Web开发特点优化的一系列解决方案。以下是前端最常用和最重要的设计模式:
📱 组件化模式
1. 容器与展示组件模式
将组件分为两类:容器组件负责业务逻辑和状态管理,展示组件只负责UI渲染。
// 容器组件
class UserListContainer extends React.Component {
state = { users: [] };
componentDidMount() {
fetchUsers().then(users => this.setState({ users }));
}
render() {
return <UserList users={this.state.users} />;
}
}
// 展示组件(无状态)
const UserList = ({ users }) => (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
2. 高阶组件模式
接收一个组件,返回一个增强的新组件。
// HOC示例:添加加载状态
const withLoading = (WrappedComponent) => {
return class extends React.Component {
state = { loading: true, data: null };
async componentDidMount() {
const data = await fetchData();
this.setState({ loading: false, data });
}
render() {
if (this.state.loading) return <Spinner />;
return <WrappedComponent {...this.props} data={this.state.data} />;
}
};
};
// 使用
const EnhancedComponent = withLoading(UserList);
3. Render Props模式
通过函数prop共享组件逻辑。
class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
// 使用
<MouseTracker render={({ x, y }) => (
<h1>鼠标位置: ({x}, {y})</h1>
)} />
4. 自定义Hook模式
React 16.8+,使用Hook共享状态逻辑。
// 自定义Hook
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// 使用
function ResponsiveComponent() {
const { width } = useWindowSize();
return <div>当前宽度: {width}px</div>;
}
🔄 状态管理模式
5. Flux/Redux单向数据流
// Redux架构模式
// 1. Action
const addTodo = (text) => ({
type: 'ADD_TODO',
payload: { text }
});
// 2. Reducer (纯函数)
const todosReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, { text: action.payload.text, completed: false }];
default:
return state;
}
};
// 3. Store
const store = createStore(todosReducer);
// 4. View (React组件连接)
connect(mapStateToProps, mapDispatchToProps)(TodoList);
6. MobX响应式状态
class TodoStore {
@observable todos = [];
@action addTodo(text) {
this.todos.push({ text, completed: false });
}
@computed get completedCount() {
return this.todos.filter(todo => todo.completed).length;
}
}
// 组件中自动响应
@observer
class TodoList extends React.Component {
render() {
return (
<div>
{store.todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</div>
);
}
}
🏗️ 架构模式
7. 微前端架构
将大型应用拆分为多个独立部署的微应用。
// 主应用 shell
class MainApp extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<header>主应用头部</header>
<micro-app-a></micro-app-a>
<micro-app-b></micro-app-b>
`;
}
}
// 微应用 A
class MicroAppA extends HTMLElement {
connectedCallback() {
this.innerHTML = `<div>微应用A的内容</div>`;
}
}
customElements.define('micro-app-a', MicroAppA);
8. 领域驱动设计(DDD)在前端
// 领域对象
class ShoppingCart {
constructor() {
this.items = [];
this.total = 0;
}
addItem(product, quantity) {
const existingItem = this.items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({ product, quantity });
}
this.calculateTotal();
}
calculateTotal() {
this.total = this.items.reduce((sum, item) => {
return sum + (item.product.price * item.quantity);
}, 0);
}
// 业务规则
canCheckout() {
return this.items.length > 0 && this.total > 0;
}
}
🎨 UI模式
9. 复合组件模式
创建一组协同工作的组件。
// 复合组件示例:标签页
const Tabs = ({ children }) => {
const [activeIndex, setActiveIndex] = useState(0);
return (
<div className="tabs">
<div className="tab-list">
{React.Children.map(children, (child, index) => (
<TabHeader
isActive={index === activeIndex}
onClick={() => setActiveIndex(index)}
>
{child.props.title}
</TabHeader>
))}
</div>
<div className="tab-content">
{React.Children.toArray(children)[activeIndex]}
</div>
</div>
);
};
// 子组件
const Tab = ({ children }) => <div>{children}</div>;
// 使用
<Tabs>
<Tab title="首页">首页内容</Tab>
<Tab title="关于">关于内容</Tab>
</Tabs>
10. 受控/非受控组件模式
// 受控组件
function ControlledInput() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}
// 非受控组件(通过ref访问)
function UncontrolledInput() {
const inputRef = useRef();
const handleSubmit = () => {
console.log(inputRef.current.value);
};
return (
<>
<input ref={inputRef} />
<button onClick={handleSubmit}>提交</button>
</>
);
}
🔧 性能优化模式
11. 虚拟滚动
class VirtualScroll extends React.Component {
state = { scrollTop: 0 };
handleScroll = (e) => {
this.setState({ scrollTop: e.target.scrollTop });
};
render() {
const { items, itemHeight, visibleHeight } = this.props;
const { scrollTop } = this.state;
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(visibleHeight / itemHeight) + 2,
items.length
);
const visibleItems = items.slice(startIndex, endIndex);
const offsetY = startIndex * itemHeight;
return (
<div
className="scroll-container"
onScroll={this.handleScroll}
style={{ height: visibleHeight }}
>
<div style={{ height: items.length * itemHeight }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map(item => (
<div key={item.id} style={{ height: itemHeight }}>
{item.content}
</div>
))}
</div>
</div>
</div>
);
}
}
12. 懒加载/代码分割
// React.lazy + Suspense
const LazyComponent = React.lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<LazyComponent />
</Suspense>
);
}
// 图片懒加载
const LazyImage = ({ src, alt }) => {
const [isLoaded, setIsLoaded] = useState(false);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting && !isLoaded) {
imgRef.current.src = src;
setIsLoaded(true);
}
},
{ threshold: 0.1 }
);
observer.observe(imgRef.current);
return () => observer.disconnect();
}, [src, isLoaded]);
return (
<img
ref={imgRef}
alt={alt}
style={{ opacity: isLoaded ? 1 : 0.3 }}
/>
);
};
🚀 异步处理模式
13. Promise模式链
// Promise链式操作
class ApiService {
async fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
}
// 并行请求
async fetchMultiple(urls) {
return Promise.all(
urls.map(url => this.fetchWithRetry(url))
);
}
// 批量处理
async batchProcess(items, batchSize = 10, processor) {
const batches = [];
for (let i = 0; i < items.length; i += batchSize) {
batches.push(items.slice(i, i + batchSize));
}
const results = [];
for (const batch of batches) {
const batchResults = await Promise.all(
batch.map(item => processor(item))
);
results.push(...batchResults);
}
return results;
}
}
14. 观察者/发布订阅模式
class EventBus {
constructor() {
this.events = new Map();
}
on(event, callback) {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event).push(callback);
}
off(event, callback) {
if (this.events.has(event)) {
const callbacks = this.events.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
emit(event, data) {
if (this.events.has(event)) {
this.events.get(event).forEach(callback => {
try {
callback(data);
} catch (error) {
console.error(`Error in event ${event}:`, error);
}
});
}
}
once(event, callback) {
const onceCallback = (data) => {
callback(data);
this.off(event, onceCallback);
};
this.on(event, onceCallback);
}
}
// 使用
const bus = new EventBus();
bus.on('user-login', (user) => {
console.log('用户登录:', user);
});
🧩 组合模式
15. 策略模式与组合
// 表单验证策略
const validationStrategies = {
required: (value) => ({
isValid: !!value,
message: '此字段为必填项'
}),
email: (value) => ({
isValid: /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
message: '请输入有效的邮箱地址'
}),
minLength: (value, min) => ({
isValid: value.length >= min,
message: `长度至少为${min}个字符`
}),
custom: (value, validator) => validator(value)
};
class FormValidator {
constructor(strategies = validationStrategies) {
this.strategies = strategies;
}
validate(field, value, rules) {
const errors = [];
rules.forEach(rule => {
const { type, params = [], message } = rule;
const strategy = this.strategies[type];
if (strategy) {
const result = strategy(value, ...params);
if (!result.isValid) {
errors.push(message || result.message);
}
}
});
return errors;
}
}
// 使用
const validator = new FormValidator();
const errors = validator.validate('email', 'test@', [
{ type: 'required' },
{ type: 'email' }
]);
16. 装饰器模式(ES7)
// 类装饰器
function logMethodCalls(target) {
const methods = Object.getOwnPropertyNames(target.prototype);
methods.forEach(methodName => {
if (methodName !== 'constructor') {
const originalMethod = target.prototype[methodName];
target.prototype[methodName] = function(...args) {
console.log(`调用 ${methodName},参数:`, args);
const result = originalMethod.apply(this, args);
console.log(`返回值:`, result);
return result;
};
}
});
return target;
}
// 方法装饰器
function debounce(delay = 300) {
return function(target, key, descriptor) {
let timeout;
const original = descriptor.value;
descriptor.value = function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
original.apply(this, args);
}, delay);
};
return descriptor;
};
}
// 使用
@logMethodCalls
class ApiService {
@debounce(500)
search(query) {
return fetch(`/api/search?q=${query}`);
}
}
📊 前端设计模式选择指南
| 场景 | 推荐模式 | 优点 |
|---|---|---|
| 复用UI逻辑 | HOC/Render Props | 逻辑复用,组件解耦 |
| 状态共享 | Context/Redux/MobX | 状态管理集中化 |
| 性能优化 | 虚拟滚动/懒加载 | 减少渲染压力 |
| 表单处理 | 受控组件 + 策略模式 | 验证灵活,状态可控 |
| 异步操作 | Promise链 + 错误边界 | 错误处理完整 |
| 组件通信 | 事件总线/发布订阅 | 跨组件通信解耦 |
| 复杂UI | 复合组件/插槽 | 组件结构清晰 |
🎯 最佳实践建议
- 保持组件单一职责:每个组件只做一件事
- 优先使用函数组件+Hook:代码更简洁,逻辑更清晰
- 合理使用设计模式:不要过度设计,简单的场景用简单方案
- 关注性能:使用React.memo、useMemo、useCallback等优化
- 类型安全:TypeScript能帮助避免很多设计错误
- 测试友好:设计模式应便于单元测试
📚 现代前端框架内置模式
React特性对应的模式:
- Hooks → 自定义Hook模式
- Context → Provider模式
- Suspense → 异步加载模式
- Portal → 弹层/模态框模式
Vue特性对应的模式:
- Mixins → 混入模式
- Slots → 插槽模式
- Composables → 组合式函数模式
这些设计模式在前端开发中非常实用,能够帮助你构建更可维护、可扩展的应用程序。