前端设计模式详解
前端设计模式全面解析
一、设计模式概述
1.1 什么是设计模式
设计模式是针对特定上下文的常见问题的可重用解决方案。在前端开发中,它们帮助我们构建可维护、可扩展、可重用的代码。
1.2 设计模式分类
- 创建型模式:处理对象创建机制
- 结构型模式:处理对象组合方式
- 行为型模式:处理对象间的通信和责任分配
二、创建型模式
2.1 工厂模式(Factory Pattern)
将对象创建逻辑封装起来。
// 简单工厂classButtonFactory{createButton(type){switch(type){case'primary':returnnewPrimaryButton();case'secondary':returnnewSecondaryButton();default:thrownewError('Unknown button type');}}}// 工厂方法classDialog{createButton(){}// 抽象方法render(){const button =this.createButton(); button.render();}}classWindowsDialogextendsDialog{createButton(){returnnewWindowsButton();}}classWebDialogextendsDialog{createButton(){returnnewWebButton();}}2.2 建造者模式(Builder Pattern)
分步骤构建复杂对象。
classCar{constructor(){this.engine =null;this.seats =0;this.gps =false;}toString(){return`Car with ${this.engine} engine, ${this.seats} seats, GPS: ${this.gps}`;}}classCarBuilder{constructor(){this.car =newCar();}setEngine(engine){this.car.engine = engine;returnthis;}setSeats(seats){this.car.seats = seats;returnthis;}setGPS(hasGPS){this.car.gps = hasGPS;returnthis;}build(){returnthis.car;}}// 使用const car =newCarBuilder().setEngine('V8').setSeats(4).setGPS(true).build(); console.log(car.toString());2.3 单例模式(Singleton Pattern)
确保一个类只有一个实例。
classSingleton{static #instance =null;constructor(){if(Singleton.#instance){return Singleton.#instance;} Singleton.#instance =this;// 初始化代码this.config ={};}staticgetInstance(){if(!Singleton.#instance){ Singleton.#instance =newSingleton();}return Singleton.#instance;}setConfig(key, value){this.config[key]= value;}getConfig(key){returnthis.config[key];}}// 使用const instance1 = Singleton.getInstance();const instance2 = Singleton.getInstance(); console.log(instance1 === instance2);// true2.4 原型模式(Prototype Pattern)
通过克隆现有对象创建新对象。
classComponentPrototype{constructor(name, styles ={}){this.name = name;this.styles = styles;this.children =[];}clone(){const clone =newComponentPrototype(this.name); clone.styles ={...this.styles }; clone.children =[...this.children];return clone;}addChild(child){this.children.push(child);}render(){ console.log(`Rendering ${this.name} with styles:`,this.styles);}}// 使用const baseButton =newComponentPrototype('Button',{ color:'blue', padding:'10px'});const primaryButton = baseButton.clone(); primaryButton.name ='Primary Button'; primaryButton.styles.color ='green';const secondaryButton = baseButton.clone(); secondaryButton.styles.color ='gray';三、结构型模式
3.1 适配器模式(Adapter Pattern)
让不兼容的接口能够一起工作。
// 旧系统classOldPaymentSystem{processPayment(amountInDollars){ console.log(`Processing $${amountInDollars} payment`);returntrue;}}// 新系统(期望欧元)classNewPaymentSystem{processPayment(amountInEuros){ console.log(`Processing €${amountInEuros} payment`);returntrue;}}// 适配器classPaymentAdapter{constructor(oldSystem){this.oldSystem = oldSystem;}processPayment(amountInEuros){// 转换欧元为美元const amountInDollars = amountInEuros *1.2;returnthis.oldSystem.processPayment(amountInDollars);}}// 使用const oldSystem =newOldPaymentSystem();const adapter =newPaymentAdapter(oldSystem); adapter.processPayment(100);// 转换为 $1203.2 装饰器模式(Decorator Pattern)
动态添加功能到对象。
// 基础组件classTextComponent{constructor(text){this.text = text;}render(){returnthis.text;}}// 装饰器基类classTextDecorator{constructor(component){this.component = component;}render(){returnthis.component.render();}}// 具体装饰器classBoldDecoratorextendsTextDecorator{render(){return`<b>${super.render()}</b>`;}}classItalicDecoratorextendsTextDecorator{render(){return`<i>${super.render()}</i>`;}}classLinkDecoratorextendsTextDecorator{constructor(component, url){super(component);this.url = url;}render(){return`<a href="${this.url}">${super.render()}</a>`;}}// 使用let text =newTextComponent('Hello World'); text =newBoldDecorator(text); text =newItalicDecorator(text); text =newLinkDecorator(text,'https://example.com'); console.log(text.render());// <a href="https://example.com"><i><b>Hello World</b></i></a>3.3 代理模式(Proxy Pattern)
控制对对象的访问。
// 真实对象classImageLoader{constructor(filename){this.filename = filename;this.loadImage();}loadImage(){ console.log(`Loading image: ${this.filename}`);// 模拟耗时的加载this.image =`Image data for ${this.filename}`;}display(){ console.log(`Displaying: ${this.image}`);}}// 代理classImageProxy{constructor(filename){this.filename = filename;this.realImage =null;}display(){if(!this.realImage){this.realImage =newImageLoader(this.filename);}this.realImage.display();}}// 使用const image1 =newImageProxy('photo1.jpg');const image2 =newImageProxy('photo2.jpg');// 图片不会立即加载 image1.display();// 加载并显示 image1.display();// 直接显示(已缓存) image2.display();// 加载并显示3.4 外观模式(Facade Pattern)
简化复杂系统的接口。
// 复杂子系统classCPU{freeze(){ console.log('CPU freeze');}jump(position){ console.log(`CPU jump to ${position}`);}execute(){ console.log('CPU execute');}}classMemory{load(position, data){ console.log(`Memory load ${data} at ${position}`);}}classHardDrive{read(lba, size){const data =`data from sector ${lba}`; console.log(`HardDrive read: ${data}`);return data;}}// 外观classComputerFacade{constructor(){this.cpu =newCPU();this.memory =newMemory();this.hardDrive =newHardDrive();}start(){ console.log('Computer starting...');this.cpu.freeze();const bootData =this.hardDrive.read(0,1024);this.memory.load(0x0, bootData);this.cpu.jump(0x0);this.cpu.execute(); console.log('Computer started!');}}// 使用const computer =newComputerFacade(); computer.start();3.5 组合模式(Composite Pattern)
以树形结构组合对象。
// 组件接口classUIComponent{constructor(name){this.name = name;this.children =[];}add(component){this.children.push(component);}remove(component){const index =this.children.indexOf(component);if(index >-1){this.children.splice(index,1);}}render(){ console.log(`Rendering ${this.name}`);this.children.forEach(child=> child.render());}}// 叶子节点classButtonextendsUIComponent{constructor(name){super(name);}render(){ console.log(`[Button] ${this.name}`);}}classInputextendsUIComponent{constructor(name){super(name);}render(){ console.log(`[Input] ${this.name}`);}}// 使用const form =newUIComponent('Login Form');const usernameInput =newInput('Username');const passwordInput =newInput('Password');const submitButton =newButton('Submit'); form.add(usernameInput); form.add(passwordInput); form.add(submitButton);const panel =newUIComponent('Main Panel'); panel.add(form); panel.render();四、行为型模式
4.1 观察者模式(Observer Pattern)
对象间的一对多依赖关系。
// 观察者接口classObserver{update(data){}}// 主题classSubject{constructor(){this.observers =[];}subscribe(observer){this.observers.push(observer);}unsubscribe(observer){const index =this.observers.indexOf(observer);if(index >-1){this.observers.splice(index,1);}}notify(data){this.observers.forEach(observer=> observer.update(data));}}// 具体观察者classUserInterfaceextendsObserver{constructor(name){super();this.name = name;}update(data){ console.log(`${this.name} received:`, data);}}// 使用const newsPublisher =newSubject();const ui1 =newUserInterface('Mobile App');const ui2 =newUserInterface('Web Dashboard');const ui3 =newUserInterface('Email Service'); newsPublisher.subscribe(ui1); newsPublisher.subscribe(ui2); newsPublisher.subscribe(ui3); newsPublisher.notify('New product launched!'); newsPublisher.unsubscribe(ui2); newsPublisher.notify('Special discount available!');4.2 发布-订阅模式(Pub/Sub Pattern)
观察者模式的变体,使用消息通道。
classEventBus{constructor(){this.events ={};}subscribe(event, callback){if(!this.events[event]){this.events[event]=[];}this.events[event].push(callback);}unsubscribe(event, callback){if(!this.events[event])return;this.events[event]=this.events[event].filter(cb=> cb !== callback);}publish(event, data){if(!this.events[event])return;this.events[event].forEach(callback=>callback(data));}}// 使用const bus =newEventBus();// 订阅者constlogger=data=> console.log(`[LOG] ${data}`);constnotifier=data=> console.log(`[NOTIFY] ${data}`);constanalyzer=data=> console.log(`[ANALYZE] ${JSON.stringify(data)}`);// 订阅事件 bus.subscribe('user.login', logger); bus.subscribe('user.login', notifier); bus.subscribe('user.purchase', analyzer);// 发布事件 bus.publish('user.login',{ username:'john', time:newDate()}); bus.publish('user.purchase',{ item:'Book', price:29.99});4.3 策略模式(Strategy Pattern)
定义一系列算法,使它们可以互换。
// 策略接口classPaymentStrategy{pay(amount){}}// 具体策略classCreditCardPaymentextendsPaymentStrategy{constructor(cardNumber, cvv){super();this.cardNumber = cardNumber;this.cvv = cvv;}pay(amount){ console.log(`Paid $${amount} with credit card ${this.cardNumber}`);}}classPayPalPaymentextendsPaymentStrategy{constructor(email){super();this.email = email;}pay(amount){ console.log(`Paid $${amount} with PayPal ${this.email}`);}}classCryptoPaymentextendsPaymentStrategy{constructor(walletAddress){super();this.walletAddress = walletAddress;}pay(amount){ console.log(`Paid $${amount} with crypto to ${this.walletAddress}`);}}// 上下文classShoppingCart{constructor(){this.items =[];this.paymentStrategy =null;}addItem(item, price){this.items.push({ item, price });}setPaymentStrategy(strategy){this.paymentStrategy = strategy;}checkout(){const total =this.items.reduce((sum, item)=> sum + item.price,0); console.log(`Total: $${total}`);if(this.paymentStrategy){this.paymentStrategy.pay(total);this.items =[];}else{thrownewError('Payment strategy not set');}}}// 使用const cart =newShoppingCart(); cart.addItem('Book',25); cart.addItem('Mouse',40); cart.setPaymentStrategy(newCreditCardPayment('1234-5678-9012-3456','123')); cart.checkout(); cart.addItem('Keyboard',80); cart.setPaymentStrategy(newPayPalPayment('[email protected]')); cart.checkout();4.4 状态模式(State Pattern)
对象内部状态改变时改变其行为。
// 状态接口classOrderState{constructor(order){this.order = order;}next(){}previous(){}cancel(){}}// 具体状态classPendingStateextendsOrderState{next(){this.order.setState(newProcessingState(this.order)); console.log('Order moved to processing');}cancel(){this.order.setState(newCancelledState(this.order)); console.log('Order cancelled');}}classProcessingStateextendsOrderState{next(){this.order.setState(newShippedState(this.order)); console.log('Order shipped');}previous(){this.order.setState(newPendingState(this.order)); console.log('Order moved back to pending');}}classShippedStateextendsOrderState{next(){this.order.setState(newDeliveredState(this.order)); console.log('Order delivered');}previous(){this.order.setState(newProcessingState(this.order)); console.log('Order moved back to processing');}}classDeliveredStateextendsOrderState{// 最终状态}classCancelledStateextendsOrderState{// 最终状态}// 上下文classOrder{constructor(){this.state =newPendingState(this);}setState(state){this.state = state;}next(){this.state.next();}previous(){this.state.previous();}cancel(){this.state.cancel();}}// 使用const order =newOrder(); order.next();// Pending -> Processing order.next();// Processing -> Shipped order.next();// Shipped -> Deliveredconst order2 =newOrder(); order2.next();// Pending -> Processing order2.cancel();// Processing -> Cancelled4.5 命令模式(Command Pattern)
将请求封装为对象。
// 命令接口classCommand{execute(){}undo(){}}// 具体命令classLightOnCommandextendsCommand{constructor(light){super();this.light = light;}execute(){this.light.turnOn();}undo(){this.light.turnOff();}}classThermostatUpCommandextendsCommand{constructor(thermostat){super();this.thermostat = thermostat;this.previousTemp =0;}execute(){this.previousTemp =this.thermostat.getTemperature();this.thermostat.setTemperature(this.previousTemp +2);}undo(){this.thermostat.setTemperature(this.previousTemp);}}// 接收者classLight{turnOn(){ console.log('Light is ON');}turnOff(){ console.log('Light is OFF');}}classThermostat{constructor(){this.temperature =20;}getTemperature(){returnthis.temperature;}setTemperature(temp){this.temperature = temp; console.log(`Thermostat set to ${temp}°C`);}}// 调用者classRemoteControl{constructor(){this.commands =[];this.history =[];}setCommand(command){this.commands.push(command);}pressButton(index){if(index <this.commands.length){const command =this.commands[index]; command.execute();this.history.push(command);}}pressUndo(){if(this.history.length >0){const command =this.history.pop(); command.undo();}}}// 使用const remote =newRemoteControl();const light =newLight();const thermostat =newThermostat();const lightOn =newLightOnCommand(light);const tempUp =newThermostatUpCommand(thermostat); remote.setCommand(lightOn); remote.setCommand(tempUp); remote.pressButton(0);// Light on remote.pressButton(1);// Temperature up remote.pressUndo();// Temperature down remote.pressUndo();// Light off五、前端特定模式
5.1 模块模式(Module Pattern)
使用闭包创建私有作用域。
const UserModule =(function(){// 私有变量let users =[];let userCount =0;// 私有方法functiongenerateId(){return Date.now().toString(36)+ Math.random().toString(36).substr(2);}// 公共接口return{addUser(name, email){const user ={ id:generateId(), name, email, createdAt:newDate()}; users.push(user); userCount++;return user.id;},getUser(id){return users.find(user=> user.id === id);},getAllUsers(){return[...users];},getUserCount(){return userCount;},// 仅返回公开数据getPublicUserData(id){const user =this.getUser(id);if(user){const{ email,...publicData }= user;return publicData;}returnnull;}};})();// 使用const userId = UserModule.addUser('John','[email protected]'); console.log(UserModule.getPublicUserData(userId));5.2 混入模式(Mixin Pattern)
通过组合扩展类功能。
// MixinsconstLoggable=Base=>classextends Base {log(message){ console.log(`[${this.constructor.name}] ${message}`);}};constSerializable=Base=>classextends Base {serialize(){returnJSON.stringify(this);}staticdeserialize(json){const data =JSON.parse(json);returnnewthis(data);}};constValidatable=Base=>classextends Base {validate(){const errors =[];// 检查必需字段if(this.requiredFields){this.requiredFields.forEach(field=>{if(!this[field]){ errors.push(`${field} is required`);}});}return{ isValid: errors.length ===0, errors };}};// 基础类classModel{constructor(data ={}){ Object.assign(this, data);}save(){ console.log('Saving model...');}}// 应用MixinsclassUserextendsSerializable(Validatable(Loggable(Model))){constructor(data){super(data);this.requiredFields =['name','email'];}}// 使用const user =newUser({ name:'John Doe', email:'[email protected]'}); user.log('Created user'); console.log(user.validate());const json = user.serialize();const user2 = User.deserialize(json);5.3 中间件模式(Middleware Pattern)
处理请求的管道。
classMiddlewarePipeline{constructor(){this.middlewares =[];}use(middleware){this.middlewares.push(middleware);}asyncexecute(context, finalHandler){let index =-1;constdispatch=asynci=>{if(i <= index){thrownewError('next() called multiple times');} index = i;let fn =this.middlewares[i];if(i ===this.middlewares.length){ fn = finalHandler;}if(!fn){return;}awaitfn(context,()=>dispatch(i +1));};returndispatch(0);}}// 使用示例:HTTP请求处理const pipeline =newMiddlewarePipeline();// 中间件 pipeline.use(async(ctx, next)=>{ console.log('Middleware 1: Logging request'); ctx.startTime = Date.now();awaitnext();const duration = Date.now()- ctx.startTime; console.log(`Middleware 1: Request took ${duration}ms`);}); pipeline.use(async(ctx, next)=>{ console.log('Middleware 2: Authentication'); ctx.user ={ id:1, name:'John'};awaitnext();}); pipeline.use(async(ctx, next)=>{ console.log('Middleware 3: Authorization');if(!ctx.user){thrownewError('Unauthorized');}awaitnext();});// 最终处理函数asyncfunctionfinalHandler(ctx){ console.log('Final handler: Processing request'); console.log('User:', ctx.user); ctx.response ={ success:true, data:'Some data'};}// 执行const context ={ request:{ url:'/api/data'}}; pipeline.execute(context, finalHandler).then(()=> console.log('Response:', context.response)).catch(err=> console.error('Error:', err.message));5.4 组件模式(Component Pattern)
// 基础组件类classComponent{constructor(props ={}){this.props = props;this.state ={};this.children =[];}setState(newState){this.state ={...this.state,...newState };this.render();}addChild(child){this.children.push(child);}render(){thrownewError('render() must be implemented');}mount(container){this.container = container;this.render();}}// 具体组件classButtonextendsComponent{constructor(props){super(props);this.state ={ clicked:false};}handleClick=()=>{this.setState({ clicked:true});if(this.props.onClick){this.props.onClick();}};render(){const button = document.createElement('button'); button.textContent =this.props.label ||'Click me'; button.style.backgroundColor =this.state.clicked ?'green':'blue'; button.style.color ='white'; button.style.padding ='10px 20px'; button.onclick =this.handleClick;if(this.container){this.container.innerHTML ='';this.container.appendChild(button);}return button;}}classFormextendsComponent{render(){const form = document.createElement('form'); form.style.padding ='20px'; form.style.border ='1px solid #ccc';this.children.forEach(child=>{const element = child.render(); form.appendChild(element);});if(this.container){this.container.innerHTML ='';this.container.appendChild(form);}return form;}}// 使用const submitButton =newButton({ label:'Submit',onClick:()=> console.log('Submitted!')});const cancelButton =newButton({ label:'Cancel',onClick:()=> console.log('Cancelled!')});const form =newForm(); form.addChild(submitButton); form.addChild(cancelButton);// 挂载到DOM form.mount(document.getElementById('app'));六、React/Vue中的设计模式
6.1 高阶组件(HOC)- React
// 高阶组件 const withLoading = (WrappedComponent) => { return function WithLoadingComponent({ isLoading, ...props }) { if (isLoading) { return <div className="loading">Loading...</div>; } return <WrappedComponent {...props} />; }; }; const withError = (WrappedComponent) => { return function WithErrorComponent({ error, ...props }) { if (error) { return <div className="error">Error: {error.message}</div>; } return <WrappedComponent {...props} />; }; }; // 使用 const DataDisplay = ({ data }) => ( <div className="data">{JSON.stringify(data)}</div> ); const EnhancedDataDisplay = withError(withLoading(DataDisplay)); // 使用示例 <EnhancedDataDisplay isLoading={loading} error={error} data={data} /> 6.2 渲染属性(Render Props)- React
class MouseTracker extends React.Component { state = { x: 0, y: 0 }; handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }); }; render() { return ( <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div> ); } } // 使用 <MouseTracker render={({ x, y }) => ( <h1>The mouse position is ({x}, {y})</h1> )} /> 6.3 提供者模式(Provider Pattern)
// React Contextconst ThemeContext = React.createContext();classThemeProviderextendsReact.Component{ state ={ theme:'light'};toggleTheme=()=>{this.setState(prev=>({ theme: prev.theme ==='light'?'dark':'light'}));};render(){return(<ThemeContext.Provider value={{ theme:this.state.theme, toggleTheme:this.toggleTheme }}>{this.props.children}</ThemeContext.Provider>);}}// 消费constThemedButton=()=>(<ThemeContext.Consumer>{({ theme, toggleTheme })=>(<button style={{ backgroundColor: theme ==='dark'?'#333':'#fff', color: theme ==='dark'?'#fff':'#333'}} onClick={toggleTheme}> Toggle Theme </button>)}</ThemeContext.Consumer>);6.4 Vue组合式函数(Composables)
// useMouse.js - Vue 3 Composition APIimport{ ref, onMounted, onUnmounted }from'vue';exportfunctionuseMouse(){const x =ref(0);const y =ref(0);constupdate=event=>{ x.value = event.pageX; y.value = event.pageY;};onMounted(()=> window.addEventListener('mousemove', update));onUnmounted(()=> window.removeEventListener('mousemove', update));return{ x, y };}// useFetch.jsimport{ ref }from'vue';exportfunctionuseFetch(url){const data =ref(null);const error =ref(null);const loading =ref(false);constfetchData=async()=>{ loading.value =true;try{const response =awaitfetch(url); data.value =await response.json();}catch(err){ error.value = err;}finally{ loading.value =false;}};fetchData();return{ data, error, loading, refetch: fetchData };}七、设计模式选择指南
| 模式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 工厂模式 | 需要创建多种类似对象 | 封装创建逻辑,易于扩展 | 增加系统复杂度 |
| 单例模式 | 全局唯一实例(配置、日志) | 节省内存,全局访问 | 测试困难,全局状态污染 |
| 观察者模式 | 一对多依赖关系 | 松耦合,易于扩展 | 可能引起内存泄漏 |
| 装饰器模式 | 动态添加功能 | 灵活,符合开放封闭原则 | 多层装饰复杂 |
| 策略模式 | 多种算法切换 | 避免条件语句,易于扩展 | 增加策略类数量 |
| 中间件模式 | 处理请求管道 | 关注点分离,易于测试 | 可能影响性能 |
八、最佳实践
- 不要过度设计:简单的代码比复杂的设计模式更好
- 模式组合使用:实际项目中经常组合多种模式
- 考虑可测试性:设计模式应提高代码的可测试性
- 保持一致性:团队内统一使用某些模式
- 文档化:对使用的设计模式进行文档说明
九、现代JavaScript中的新特性
9.1 Proxy和Reflect实现模式
// 使用Proxy实现数据绑定functioncreateObservable(obj, callback){returnnewProxy(obj,{set(target, property, value){const oldValue = target[property]; target[property]= value;callback(property, oldValue, value);returntrue;},get(target, property){ console.log(`Getting ${property}`);return target[property];}});}const user =createObservable({ name:'John', age:30},(prop, oldVal, newVal)=>{ console.log(`${prop} changed from ${oldVal} to ${newVal}`);}); user.name ='Jane';// 触发回调9.2 使用ES6+特性简化模式
// 使用Symbol实现私有属性const _private =Symbol('private');classMyClass{constructor(){this[_private]='secret';}getSecret(){returnthis[_private];}}// 使用Generator实现迭代器模式function*paginate(items, pageSize){for(let i =0; i < items.length; i += pageSize){yield items.slice(i, i + pageSize);}}const items =[1,2,3,4,5,6,7,8,9];for(const page ofpaginate(items,3)){ console.log('Page:', page);}设计模式是解决常见问题的工具箱,理解它们有助于编写更优雅、可维护的代码。