news 2026/7/31 9:03:27

前端的设计模式?我觉得90%都是在过度设计!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端的设计模式?我觉得90%都是在过度设计!

最近Code Review的时候,我看到我们组一个很聪明的年轻同事,用观察者模式,写了一个极其复杂的全局状态订阅系统,就为了在一个组件里,响应另一个不相关的组件的点击事件。

比较常见的场景:点击 Button 组件,让 Panel 组件打印日志或显示提示,具体伪代码👇:

// observer.jsclassObserver{constructor(){this.subscribers=[];}subscribe(fn){this.subscribers.push(fn);}unsubscribe(fn){this.subscribers=this.subscribers.filter(sub=>sub!==fn);}notify(data){this.subscribers.forEach(fn=>fn(data));}}// 全局状态中心(相当于单例)exportconstglobalClickObserver=newObserver();
// Button.jsximportReactfrom"react";import{globalClickObserver}from"./observer";exportdefaultfunctionButton(){consthandleClick=()=>{console.log("Button clicked");globalClickObserver.notify({source:"Button",payload:"Hello Panel"});};return<buttononClick={handleClick}>Click</button>;}
// Panel.jsximportReact,{useEffect}from"react";import{globalClickObserver}from"./observer";exportdefaultfunctionPanel(){useEffect(()=>{constsubscriber=(data)=>{if(data.source==="Button"){console.log("event:",data.payload);}};globalClickObserver.subscribe(subscriber);return()=>globalClickObserver.unsubscribe(subscriber);},[]);return<div>I'm Panel</div>;}

我把他叫过来,问他为什么不直接用一个简单的Event Bus(比如mitt),或者干脆用Zustand这样的状态管理器。

他说:“我觉得用设计模式,代码的扩展性会更好,也显得更高级😂。”

这个瞬间,让我下定决心,想聊聊这个话题:

在现代前端开发(尤其是React/Vue)中,我们挂在嘴边的那些经典设计模式,90%都是在过度设计。

在我开喷之前,请允许我澄清:我反对的不是设计思想,比如高内聚低耦合单一职责。我反对的是,把那些20年前为Java/C++总结的、沉重的、面向对象的大招,生搬硬套到我们现代前端的开发范式里。


我们为什么会陷入设计模式的陷阱?

曾几何时,我也曾是设计模式的忠实信徒。热衷于在代码里寻找应用工厂模式、策略模式的场景。

我们之所以会这样,我觉得原因有二:

为了应对面试

设计模式是前端面试八股文里的重灾区。为了通过面试,我们不得不去背诵它们的定义和用法,这就导致了一种为了应考的惯性思维。

看起来牛皮🤷‍♂️

我们总觉得,能说出几个设计模式的名字,能把它们用在代码里,就代表自己的水平更高。仿佛不说个单例、不聊个装饰器,就体现不出自己的资深。


有哪些水土不服的设计模式?

我们来看几个在前端领域最常被滥用的经典模式。

单例模式

经典写法:搞一个class,一个私有构造函数,再加一个getInstance的静态方法,防止被多次new

我的吐槽点别闹了,我们有ES6模块!!!

前端的原生模式:JavaScript的import/export机制,天生就是单例的。你export一个实例,在所有地方import它,它从始至终就是同一个实例。

// a.jsclassMyService{/* ... */}// 导出一个实例exportconstmyServiceInstance=newMyService();// b.jsimport{myServiceInstance}from'./a.js';// c.jsimport{myServiceInstance}from'./a.js';// b.js和c.js里的myServiceInstance,是同一个东西

为了实现单例,而去手写一个Singleton类,在现代前端里,属于(省略一万字...)。

工厂模式

写法:写一个create函数,根据传入的typenew出不同的类的实例 ?。

在React/Vue里,我们有比工厂更强大、更直观的武器——组件。你根本不需要一个create函数,你只需要一个组件,通过props来决定它的形态和行为。

// 你不需要一个 createButton 的工厂// 你只需要一个 Button 组件functionButton({kind,...props}){if(kind==='icon'){return<IconButton{...props}/>;}if(kind==='text'){return<TextButton{...props}/>;}return<PrimaryButton{...props}/>;}

用组件思维去思考,比工厂思维更符合现代前端的直觉。

观察者模式

写法:维护一个订阅者列表(subscribers),提供subscribeunsubscribenotify方法?

我的吐槽点是你的框架自带的响应式系统,比你手写的强一百倍。

React的useState/useEffect,Vue的ref/watch,它们本身就是更高阶、更强大的响应式系统,是观察者模式的终极体现。状态(被观察者)变化,UI(观察者)自动更新。你为什么要去手写一个简陋版的伪响应式,而不用框架自带的、经过千锤百炼的完整经验呢?


那剩下 10%有用的,是什么?

我喷了90%,那剩下10%依然有价值的是什么?在我看来,是一些设计思想,而不是具体的什么大招。

发布/订阅模式 (Pub/Sub)

它和观察者模式很像,但更解耦。当两个完全不相关的组件需要通信,而你又不想为此引入一个全局状态库时,一个轻量级的事件总线(Event Bus)或者mitt,就非常有用。

// pubsub.jsclassPubSub{constructor(){this.events={};// 存储事件和对应的订阅者回调}// 订阅subscribe(event,callback){if(!this.events[event]){this.events[event]=[];}this.events[event].push(callback);return()=>this.unsubscribe(event,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));}}// 导出一个全局单例exportconstpubsub=newPubSub();

策略模式
这个模式的核心思想——将不同的算法封装起来,使它们可以互相替换——在前端依然非常闪光。它能帮助我们写出更优雅、更易扩展的代码,用来代替冗长的if/elseswitch

// 比如,处理不同类型的用户折扣conststrategies={'normal':(price)=>price,'vip':(price)=>price*0.8,'svip':(price)=>price*0.6,};functioncalculatePrice(userType,price){returnstrategies[userType](price);}

你看,这里没有class,没有那么复杂的逻辑,但它蕴含了策略模式的思想。


作为组长,当我在Code Review 里看到一个同事用了工厂模式时,我不会觉得他很牛逼。我反而会警惕:他是不是为了炫技?而选择了一个更复杂的方案?我们能不能用一个简单的React组件,就把这事儿给干了?

现代前端框架,已经为我们内建了一套非常优秀、非常自洽的设计模式。组件是工厂,Hooks是装饰器/策略,响应式系统是观察者。

你的目标,不是写出能套上某个设计模式名字的代码,而是写出简单、清晰、易于维护的代码。在前端,后者往往比前者重要得多🤷‍♂️。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/31 9:03:02

Windows端口占用排查全攻略:从netstat到PowerShell实战

1. 端口状态排查&#xff1a;从新手到老手的必经之路 在Windows环境下搞开发、做运维&#xff0c;或者仅仅是排查一些网络服务问题&#xff0c;有一个场景你绝对绕不开&#xff1a;想知道某个端口是不是开着&#xff0c;或者想知道是哪个“家伙”占用了你心仪的端口。这听起来是…

作者头像 李华
网站建设 2026/7/31 9:01:45

网络故障排查利器:tcpdump ARP抓包实战指南

1. 从一次网络故障排查说起&#xff1a;为什么ARP抓包是基本功那天下午&#xff0c;整个开发区的网络突然变得异常卡顿&#xff0c;Ping网关的延迟从平时的1ms飙升到几百毫秒&#xff0c;还伴随着大量的丢包。运维同事初步排查了交换机、防火墙&#xff0c;都没发现明显异常。就…

作者头像 李华
网站建设 2026/7/31 9:00:18

再读人月神话:AI 时代下的产品化与系统化

再读人月神话&#xff1a;AI 时代下的产品化与系统化在 AI 时代重读读这本五十年前出版的软件工程领域圣经倒是带来了很多新的感悟&#xff0c;尤其是有了 AI 之后很多论点在我看来已经不完全成立了&#xff0c;此文记录一下自己的思考&#xff0c;权作抛砖引玉 本文对应原文的…

作者头像 李华
网站建设 2026/7/31 9:00:12

OpenCode 速通:19 万星,能自己操控浏览器的 AI 编程神器

用过 Claude Code 或者 Codex 的朋友都知道&#xff0c;接入第三方模型这件事有多折腾——装 Router、配环境变量、调参数&#xff0c;光是这套前置流程就能劝退一半人。 OpenCode 直接把这些障碍拆掉了。打开界面&#xff0c;选择你想用的任意第三方供应商&#xff0c;填上 K…

作者头像 李华
网站建设 2026/7/31 8:59:54

玉米生育期精准记录:从田间观测到农事决策的完整指南

1. 项目概述&#xff1a;为什么我们需要记录玉米生育期&#xff1f;种玉米&#xff0c;看起来是“春种一粒粟&#xff0c;秋收万颗子”的简单循环&#xff0c;但真干起来&#xff0c;你会发现从种子落地到棒子归仓&#xff0c;中间每一步都藏着大学问。我在地头跑了十几年&…

作者头像 李华
网站建设 2026/7/31 8:56:51

低成本开启 AI 布局,主流大模型商用接口稳定供应

人工智能商业化持续提速&#xff0c;大模型 API 正在成为各行各业数字化转型的核心基础设施。想要搭建 AI 应用&#xff0c;从头自研大模型需要投入巨额算力、组建专业算法团队&#xff0c;漫长的研发周期让很多中小企业望而却步。依托成熟大模型 API 服务&#xff0c;开发者与…

作者头像 李华