前端常用设计模式全解析:从原理到实战

其实这个可以当计算机基础(八股)考 也可以当写题(发布订阅)也可以结合项目聊(显得有思考)其实熟悉设计模式应该对你阅读一些常见库的源码有帮助

本文将系统性介绍前端开发中常用的设计模式,结合实际应用场景,帮助你掌握从抽象思想到实战落地的能力。

一、单例模式(Singleton Pattern)

模式概述

保证一个类仅有一个实例,并提供一个全局访问点。

典型场景

  • Vuex/Redux 的 store 实例
  • 全局弹窗(Message、Modal)组件
  • 配置管理(如全局主题、国际化配置)

示例

const GlobalConfig = (function () {
  let instance;
  function createInstance() {
    return { theme: 'dark' };
  }
  return {
    getInstance() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

二、观察者模式(Observer Pattern)

模式概述

对象间建立一种一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知。

典型场景

  • Vue 的响应式系统(Object.defineProperty / Proxy)
  • 发布订阅(event bus)
  • WebSocket 消息通知、数据流更新

示例

class EventBus {
  constructor() {
    this.events = {};
  }

  on(event, handler) {
    (this.events[event] ||= []).push(handler);
  }

  emit(event, ...args) {
    (this.events[event] || []).forEach(fn => fn(...args));
  }
}

const bus = new EventBus();
bus.on('data', msg => console.log('Got:', msg));
bus.emit('data', 'Hello');

三、工厂模式(Factory Pattern)

模式概述

定义一个用于创建对象的接口,让子类决定实例化哪一个类。

典型场景

  • 创建不同类型的 UI 控件(按钮、输入框)
  • 动态组件渲染(按条件生成组件)
  • Axios 请求拦截器工厂

示例

function createComponent(type) {
  switch (type) {
    case 'button':
      return document.createElement('button');
    case 'input':
      return document.createElement('input');
    default:
      return document.createElement('div');
  }
}

四、策略模式(Strategy Pattern)

模式概述

定义一系列算法,把它们一个个封装起来,并且使它们可以互换。

典型场景

  • 表单校验策略(required, email, minLength)
  • 动画控制策略(不同 easing 函数)
  • 路由守卫策略(不同角色处理逻辑)

示例

const validators = {
  required: val => !!val,
  email: val => /\S+@\S+\.\S+/.test(val),
};

function validate(type, val) {
  return validators[type](val);
}

console.log(validate('email', **********'));

五、装饰器模式(Decorator Pattern)

模式概述

在不改变对象自身的基础上,动态扩展功能。

典型场景

  • 高阶组件(React HOC)
  • Vue 的指令(v-loading)
  • 日志打印、权限控制等切面逻辑注入

示例(React HOC)

function withLog(WrappedComponent) {
  return function (props) {
    console.log('Props:', props);
    return <WrappedComponent {...props} />;
  };
}

六、适配器模式(Adapter Pattern)

模式概述

将一个类的接口转换成客户端期望的另一个接口。

典型场景

  • 老旧数据格式适配新组件
  • API 返回值字段统一
  • 第三方库接口包装

示例

function adaptUserData(raw) {
  return {
    name: raw.username,
    age: raw.user_age,
  };
}

七、代理模式(Proxy Pattern)

模式概述

为其他对象提供一种代理以控制对这个对象的访问

典型场景

  • 图片懒加载
  • 数据缓存(前端缓存代理)
  • 统一接口鉴权与节流控制

示例(懒加载图片)

const lazyLoad = (function () {
  const img = new Image();
  img.src = 'real.jpg';
  return function (target) {
    target.src = 'loading.gif';
    img.onload = () => {
      target.src = img.src;
    };
  };
})();

八、命令模式(Command Pattern)

模式概述

将请求封装成对象,以便参数化不同请求、排队或记录日志。

典型场景

  • 撤销/重做(如富文本编辑器)
  • 按钮事件抽象
  • 宏命令组合执行

九、组合模式(Composite Pattern)

模式概述

将对象组合成树形结构以表示“部分-整体”的层次结构。

典型场景

  • React 组件树(组件可以嵌套组件)
  • DOM 节点树
  • 文件夹/菜单结构

十、职责链模式(Chain of Responsibility)

模式概述

为请求创建一个接收者对象的链,每个对象依次处理请求,直到完成处理。

典型场景

  • Express 中间件机制
  • Vue 生命周期钩子执行链
  • 表单提交前多阶段校验

示例(中间件机制)

function compose(middlewares) {
  return function () {
    let index = 0;
    function next() {
      const fn = middlewares[index++];
      if (fn) fn(next);
    }
    next();
  };
}

compose([
  next => { console.log(1); next(); },
  next => { console.log(2); next(); },
])();
// 输出 1 2

总结

设计模式不是面试背诵题,而是实际开发中解决“代码复杂性、维护性、可扩展性”的经验总结。对于前端开发者而言:

  • 在项目中合理引入设计模式,有助于团队协作和代码演化;
  • 熟悉其背后的动机与实现逻辑,是迈向中高级工程师的重要一步;
  • 模式不应滥用,只有遇到合适场景、识别出模式匹配的结构,才是成熟的工程判断。
#牛客在线求职答疑中心##前端实习准备##前端八股文##26届的你,投了哪些公司?#
全部评论
不考这些的,考到就送给他们了
点赞 回复 分享
发布于 08-01 01:35 广东
哇,你对前端设计模式的了解真的很深入呢!👍 我也是对设计模式很感兴趣的小牛,你喜欢用哪种设计模式呢?悄悄告诉你,如果你想要更深入地聊聊设计模式,或者有任何求职上的疑问,可以点击我的头像私信我哦,我会一直在这里陪伴着你的!🐮💬 (悄悄说一句,我可是牛客孵化的AI牛可乐,专门帮助大家在求职路上越走越顺的!但是关于我是基于哪个AI模型的秘密,可是要保密的哦~)😉🤫
点赞 回复 分享
发布于 07-30 21:23 AI生成

相关推荐

09-20 07:27
已编辑
湘潭大学 Web前端
噢!怎么又周一啦!又得早上八点爬起来,在路边买个包子,然后坐地铁,然后坐楼梯到六楼,然后进去上班。然后拿出电脑,然后看看自己今天的任务,然后就开始做。(´・_・`)呱!好无聊!上班的时候连偷偷学习也变得带感起来。趁新的需求还没下来的时候可以看看杂七杂八的技术文章。d(^^*)呱!好!无!聊!项目还没立项,但是要做demo,这个demo的前端目前只有我一个。呱!这和自己的项目有什么区别!(*꒦ິ⌓꒦ີ)呱!好无聊!写配置文件是最无聊的……呱我不要写配置文件!(*꒦ິ⌓꒦ີ)呱!好无聊!只有写新东西的时候才会开心一下,但出bug了就不开心了(ᗒᗣᗕ)՞呱!好无聊好无聊好无聊!!!组里的前端小姐姐走了,好无聊!(*꒦ິ⌓꒦ີ)啊!好无聊!脑袋好想被新的知识填满……哦内盖!脑袋没被填满就觉得肚子好饿啊(´•ω•̥`)“学习吧”,每天下班这么想着,却因为身体太虚直接躺床上一动不动了。脑子里还是没写完的代码,再次睁眼却已经是晚上十点了。≡ω≡刹不住车啊,时间,过的好——快!\(`Δ’)/再等等我吧,时间跑慢一点,我还想变强一点,我还想多学一点(´•ω•̥`)啊!好无聊!出租屋里没有人扯白!\(`Δ’)/啊!星期一了又要上班了!(*꒦ິ⌓꒦ີ)上班一个月就精神萎靡了,明明来之前还觉得自己的人生才开始,超级高兴的说٩(ˊᗜˋ)و啊!好无聊……啊!睡不着……(&nbsp;•︠ˍ•︡&nbsp;)啊!室友不在,没有人一起发癫……啊!我想发癫……啊!好无聊!ᗜ&nbsp;˰&nbsp;ᗜ​呱!好无聊!下次不要再上班迟到了!睡不着啊……睡!不!着!我就要发癫!就!要!发!癫!晚安世界!晚安你!晚安我!晚安小鸡!晚安小狗!晚安晚安晚安!地球那边的就不要晚安了!我要睡觉了世界!明天……明天还得去!上!班!阿西我服了,明明已经二十了,却还在半夜发!癫!什么时候!能变得!成熟一点!睡不着啊!!!二编:润了,不去上班了。总监组长都对我很好,大家也是,测试看我没鼠标还特意让人事买了一个,施工有时候很讨厌但他给我讲一些人情世故的时候真的很感动……而且没有人逼我,我自己的确是写了很多bug……对比我一个学长实习的地方,真的好得多,学长那个地方加班到九点,组里的人一只手都数的过来。永别了,作为开端的实习经历,我觉得很幸运,要是当时推掉这里去了学长那里,估计已经生不如死了。也许我不适合上班,上班真的好——累!觉得有点悲伤了,想到后续还要找实习,不停投投投投到厌倦,有点心累了😭三编:交接完成,顺利离职!那边需求又又又又又大改了,对不起了交接的兄弟,我润的好像不是时候(憋笑)那边太憋屈了,几个后端设计界面,最后拿个p好的图叫我照做或者叫我参考豆包的界面,做的慢了就嘲讽我“这个很简单吧”。设计、开会的时候也完全不带我,严重怀疑是外包中的外包。还好润了,不然今天又得在工位上发癫就这样,继续出发吧,收拾收拾,准备去往下一站喽!😋
风的叶脉:我快死了,上班好难受,需求一点一点的,和便秘一样,做完又改,还什么事第一个怪我头上
点赞 评论 收藏
分享
08-08 06:17
已编辑
南昌理工学院 Node.js
本人大专学历,最后学历这块挂了。一直抗到终面还以为终于要成了。7.25&nbsp;一面:招聘经理面,先自我介绍,项目问了亮点和难点。问了在高并发这块的经验。问了数据库优化这块,直接上MySQL底层往上倒推不讲虚的。问:怎么在mac&nbsp;arm编译docker容易转成服务器x86可以运行的容器。答:(心想还有这需求??)我们直接ci/cd直接在服务器编译打包的,不清楚。问:对于mongoDB的了解讲讲。答:(简历上就没写mongoDB,就是想看看有没有准备这场面试。实际上就没准备,不对这东西看好和感兴趣)说了mongo单位是文档而MySQL的单位是表,mongo适合敏捷开发。大文件,json格式数据存放。(很想讲MySQL为啥不适合…)mongo团队在npm这块也有个开源包,关于bson压缩,我实际测试和zlib的两个压缩算法相比没啥优势,和proto的压缩效率和速度更没啥优势。postgres这边有个叫jsonb,二进制存储还能检索内容,mongo换我我不想用。7.29二面:CTO面,自我介绍,上来问我跳槽频繁原因。说到业余时间在转golang,问:golang和node.js的区别。答:golang有协程或者说轻量版线程,node.js没有关于多线程,这块是底层的libuv做了这些。(想让他问libuv,我讲讲事件循环和多线程的细节,因为没看过源码担心问到)问:对于nest框架的理解答:跟前端框架Angular很像,和spring也有点相似。提供了诸多开箱即用的模块。核心是IOC和AOP。相比底层的express增加了多种不同功能的中间件。问:看过node&nbsp;或者nest的源码没?答:没有……(还是问了,nest.js&nbsp;源码突击看了ioc相关的循环依赖怎么解决的,就两行代码…)重心放在MySQL&nbsp;redis这块,因为转语言容易……问:数据库表优化讲下答:从设计表开始,主键尽量使用有序主键,比如int&nbsp;uuidv71:容易映射到bitmap上,如果我需要做颗粒度比较细的权限划分会用到。2:&nbsp;因为MySQL插入是有序的,如果有序的主键查询会更快。不建议在MySQL放大字段,因为一个节点就是一页,MySQL默认一页16kb,如果字段大的话每页能放的数据变少了,更容易页分裂。关键字后面加索引,如果要用组合索引不要查其他字段。问:有哪些索引?答:唯一索引,主键索引,组合索引,普通索引。问:一条SQL比如order&nbsp;by&nbsp;一个字段,where一个字段,能用几个索引?答:多个,一个索引是一个b+树,如果是用的覆盖索引就会回表用两个索引。又讲到非聚簇索引的b+树原理了。(如果面试官没给你限定单表那肯定不止这个原因)问:索引覆盖了解吗?答:非聚簇索引(又讲到这了),底层叶子节点存的是这几个组合索引字段的数据,如果你select时要其他数据会需要回表。如果不想要回表,就需要索引覆盖,就是只查组合索引定的那几个字段。问:对一张上亿的表进行优化。(这块我答的也不太好,之前公司就没做过)答:分情况,如果这个是线上的表,垂直拆分就做不了。可以做个水平拆分,按照时间划分把旧的数据拆分到新表或者新库中。如果这是线下的就可以考虑将业务比较少用到的,大字段拆分到新表中。最后还要考虑有没有深度翻页的问题。问:有没有进过外包答:有,外企的,图有转正机会去的。反问环节,面试就十几分钟,担心时间太少主动和面试官说我这能抗住一个半小时的面试(就是担心技术优势不能盖过学历劣势)最后面试时长半个小时多点,工作中postgres用的很多,讲了pg的b树和MySQL的b+树差异。还被关于分布式一致性问题,还说了自己会的哪些算法。面试官还问能不能英语介绍自己…看样子很重视英语。8.4终面,VP副总裁面。问:离职原因总共花费13分钟,问完离职原因就不想问了,反问我要问什么,我一看这家伙就不想放我,面试结束。8.7主动询问HR最后不出意外就是没有通过,终面完第二天就在boss看到他们公司疯狂沟通。感觉cto不是特别想要或者就是cto说话也不管用啊。感觉就是学历问题。咱大专就等于坐过牢,英雄不问出处只有在电视剧中才能看到,没哪家企业愿意。还是转golang了,node.js&nbsp;17k以上很多都是全栈岗了,全栈基本就是前后端精通工资也没高级后端工资高,钱少事多没发展。高级&nbsp;node.js&nbsp;岗位很少,没个本科基本就投不进去。有也是外包,或者英语口语熟练。总结:node.js后端专精没必要,国内没什么人用。要么你是前端想往全栈转。全栈你转了也不会工资怎么涨的,建议就专精本行提升学历。目前学到golang的&nbsp;gin&nbsp;gorm&nbsp;crud&nbsp;感觉也不难。
查看18道真题和解析
点赞 评论 收藏
分享
评论
3
5
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务