设计模式
前端常用设计模式及其应用场景
📚 内容概览
创建型模式
结构型模式
- 代理模式 - Vue3 响应式、图片懒加载、缓存代理
- 装饰器模式 - HOC、装饰器语法、AOP
- 适配器模式 - 接口适配、第三方库封装
- 外观模式 - 简化复杂接口、工具库封装
- 组合模式 - 树形结构、组件树
行为型模式
- 观察者模式 - EventEmitter、发布订阅
- 策略模式 - 表单验证、权限控制
- 命令模式 - 撤销重做、宏命令
- 迭代器模式 - 遍历集合、Generator
- 责任链模式 - 中间件、事件冒泡
- 模板方法模式 - React 生命周期、抽象类
前端特有模式
- MVC/MVP/MVVM - 前端架构模式对比
- 模块模式 - 命名空间、私有变量
- 中间件模式 - Redux、Koa 中间件
- 插件模式 - 可扩展架构设计
设计原则
🎯 学习路径
初级(理解概念)
- 单例模式、工厂模式
- 观察者模式、发布订阅
- 策略模式、代理模式
中级(框架应用)
- 装饰器模式(HOC)
- 中间件模式(Redux、Koa)
- MVVM 模式(Vue、React)
高级(架构设计)
- 插件化架构设计
- SOLID 原则应用
- 设计模式组合使用
🔥 面试高频
- 说说你了解的设计模式?
- 单例模式的应用场景?如何实现?
- 观察者模式和发布订阅模式的区别?
- 什么是代理模式?Vue3 响应式原理?
- 装饰器模式在 React 中的应用?
- 什么是中间件模式?Redux 中间件原理?
- 如何设计一个插件化系统?
- SOLID 原则是什么?
📊 设计模式分类
设计模式(23种)
├── 创建型(5种)
│ ├── 单例模式 ⭐⭐⭐
│ ├── 工厂模式 ⭐⭐⭐
│ ├── 抽象工厂模式
│ ├── 建造者模式 ⭐⭐
│ └── 原型模式 ⭐⭐
│
├── 结构型(7种)
│ ├── 代理模式 ⭐⭐⭐
│ ├── 装饰器模式 ⭐⭐⭐
│ ├── 适配器模式 ⭐⭐
│ ├── 外观模式 ⭐⭐
│ ├── 组合模式 ⭐⭐
│ ├── 桥接模式
│ └── 享元模式
│
└── 行为型(11种)
├── 观察者模式 ⭐⭐⭐
├── 策略模式 ⭐⭐⭐
├── 命令模式 ⭐⭐
├── 迭代器模式 ⭐⭐
├── 责任链模式 ⭐⭐⭐
├── 模板方法模式 ⭐⭐
├── 状态模式
├── 访问者模式
├── 中介者模式
├── 备忘录模式
└── 解释器模式
⭐⭐⭐ 前端高频 ⭐⭐ 常用 ⭐ 了解即可🚀 快速开始
单例模式
javascript
// ES6 实现
class Singleton {
static instance = null;
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
}
}
// 应用:Vuex Store
const store = new Vuex.Store({
state: { count: 0 },
mutations: {
increment(state) {
state.count++;
}
}
});观察者模式
javascript
class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(...args));
}
}
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
}
}
// 应用:Vue 事件总线
const eventBus = new EventEmitter();
eventBus.on('update', (data) => console.log(data));
eventBus.emit('update', { msg: 'hello' });代理模式
javascript
// 图片懒加载
class ImageProxy {
constructor(img) {
this.img = img;
this.realImg = new Image();
}
setSrc(src) {
this.img.src = 'loading.gif';
this.realImg.onload = () => {
this.img.src = src;
};
this.realImg.src = src;
}
}
// 应用:Vue3 响应式
const proxy = new Proxy(target, {
get(target, key) {
track(target, key); // 依赖收集
return target[key];
},
set(target, key, value) {
target[key] = value;
trigger(target, key); // 触发更新
return true;
}
});装饰器模式
javascript
// React HOC
function withAuth(Component) {
return function AuthComponent(props) {
const isAuth = checkAuth();
if (!isAuth) {
return <Redirect to="/login" />;
}
return <Component {...props} />;
};
}
// 使用
const ProtectedPage = withAuth(Dashboard);策略模式
javascript
// 表单验证
const strategies = {
required: (value, errorMsg) => {
if (!value) return errorMsg;
},
minLength: (value, length, errorMsg) => {
if (value.length < length) return errorMsg;
},
email: (value, errorMsg) => {
if (!/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(value)) {
return errorMsg;
}
}
};
class Validator {
constructor() {
this.rules = [];
}
add(value, rules) {
rules.forEach(rule => {
const [strategy, ...args] = rule.split(':');
this.rules.push(() => {
return strategies[strategy](value, ...args);
});
});
}
validate() {
for (let rule of this.rules) {
const error = rule();
if (error) return error;
}
}
}中间件模式
javascript
// Redux 中间件
const logger = store => next => action => {
console.log('dispatching', action);
const result = next(action);
console.log('next state', store.getState());
return result;
};
// Koa 中间件
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});🎨 前端框架中的设计模式
React
- 组合模式:组件树
- 装饰器模式:HOC
- 观察者模式:useState、useEffect
- 策略模式:条件渲染
Vue
- 代理模式:响应式系统(Proxy)
- 观察者模式:依赖收集与派发更新
- 策略模式:指令系统
- 模板方法模式:生命周期钩子
Redux
- 单例模式:Store
- 观察者模式:subscribe
- 中间件模式:applyMiddleware
- 命令模式:Action
📖 设计原则(SOLID)
- 单一职责原则(SRP):一个类只负责一个功能
- 开闭原则(OCP):对扩展开放,对修改关闭
- 里氏替换原则(LSP):子类可以替换父类
- 接口隔离原则(ISP):使用多个专门接口,而不是单一总接口
- 依赖倒置原则(DIP):依赖抽象,而不是具体实现
🔗 推荐资源
- 《JavaScript 设计模式与开发实践》- 曾探
- 《Head First 设计模式》
- Refactoring.Guru