news 2026/10/3 17:02:24

前端精读周刊:Chain of Responsibility 职责链模式——从 JS 事件冒泡到中间件机制的设计之道

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端精读周刊:Chain of Responsibility 职责链模式——从 JS 事件冒泡到中间件机制的设计之道
  • 文档
  • 技术博客
  • 教程

【免费下载链接】weekly

前端精读周刊。帮你理解最前沿、实用的技术。

项目地址:https://gitcode.com/GitHub_Trending/we/weekly
点击查看免费下载

本篇精读文章来自仓库 设计模式/179.精读《设计模式 - Chain of Responsibility 职责链模式》.md,以 JS 事件冒泡、后端中间件机制、通用帮助文案三个日常场景切入,系统拆解职责链模式(Chain of Responsibility)的意图、结构与实现。读完你将掌握:为什么说event.stopPropagation()就是职责链的"阻断能力"、如何用十几行代码手写一个可穿透可中断的职责链,以及该模式与组合模式(Composite)组合使用、在搭建引擎中间件中的真实落地方式。

职责链模式是什么

Chain of Responsibility(职责链模式)属于行为型模式。行为型模式不仅描述对象或类的模式,还描述它们之间的通信模式——比如对操作的处理应该如何传递等等。

意图:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

几乎所有设计模式,在了解到它之前,你大概率已经在实战中遇到过了,因此设计模式的确是从实践中得出的真知。但另一方面,如果没有实战的理解,单看设计模式是枯燥的、难以理解的,因此学习设计模式时,要结合实际问题思考。

三个场景:什么时候会用到职责链

如果看不懂上面的意图介绍,没有关系,设计模式需要在日常工作里用起来,结合例子可以加深你的理解,下面三个例子能让你体会什么场景下会用到这种设计模式。

中间件机制

设想我们要为一个后端框架实现中间件(知道 Koa 的同学可以理解为 Koa 的洋葱模型),在代码中可以插入任意多个中间件,每个中间件都可以对请求与响应进行处理。

由于每个中间件只响应自己感兴趣的请求,因此只有运行时才知道这个中间件是否会处理请求。那么中间件机制应该如何设计,才能保证其功能和灵活性呢?

本仓库的 可视化搭建/275.组件值校验.md 就是一个真实佐证:搭建引擎通过createMiddleware传递中间件来拓展自定义校验规则:

import { createMiddleware } from "designer"; const myMiddleware = createMiddleware({ validateRules: { // 自定义校验规则,判断是否为空字符串 isEmptyString: (value, options?: { errorMessage?: string }) => { if (value === "") { return true; } return options.errorMessage; }, }, });

定义后即可在组件的valueValidator中使用这条自定义规则:

const input: ComponentMeta = { componentName: "input", element: Input, valueValidator: () => ({ isEmptyString: { errorMessage: "字符串必须为空", }, }), };

可以看到,校验规则的集合本身就是一条"链":内置规则在前、自定义规则在后,每个规则各自决定是否"接住"校验请求,这正是职责链模式在真实业务框架中的落地形态。

通用帮助文案

如果一个大型系统中,任何一个模块点击都会弹出帮助文案,但并不是每个模块都有帮助文案的:如果一个模块没有帮助文案,则显示其父级的帮助文案,如果再没有,就继续冒泡到整个应用,展示应用级别的兜底帮助文案。这种系统应该如何设计?

这个需求本质上就是一条"文案查找链":模块自己 → 父级模块 → 应用兜底,每一级都是链上的一个 Handler,只有前一级"不处理"时请求才继续向后传递。

JS 事件冒泡机制

其实JS 事件冒泡机制就是个典型的职责链模式,因为任何 DOM 元素都可以监听比如onClick,不仅可以自己响应事件,还可以使用event.stopPropagation()阻止继续冒泡。

意图解释:站在点击事件的角度重新理解

JS 事件冒泡机制对前端来说太常见了,但我们换个角度,站在点击事件的角度理解,就能重新发现其设计的精妙之处:

点击事件是叠加在每层 DOM 上的,由于 DOM 对事件的处理和绑定是动态的,浏览器本身不知道哪些地方会处理点击事件,但又要让每层 DOM 拥有对点击事件的"平等处理权",所以就产生了冒泡机制,与事件阻止冒泡功能。

通用帮助文案和 JS 事件冒泡很类似,只是把点击事件换成了弹出帮助文案罢了,其场景机理是一样的。

回到职责链模式的意图本身:

意图:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

逐句拆解:

  • "请求"指的是某个触发机制产生的请求,是一个通用概念。点击事件、帮助文案、HTTP 请求都可以视为"请求"。
  • "避免请求的发送者和接收者之间的耦合关系":如果我们只有一个对象有处理请求的机会,那接收者就与发送者之间耦合了——其他接收者必须通过这个接收者才能继续处理,这种模式不够灵活。
  • 后半句描述如何设计:将对象连成一条链,沿着链条传递该请求,直到有一个对象处理它为止。还要理解到,任何一个对象都拥有阻断请求继续传递的能力——对应到 JS 就是stopPropagation()。

在中间件机制的例子中,后端 Web 框架对 Http 请求的处理就是个运用职责链模式的典型案例,因为后端框架要处理的请求是平行关系,任何请求都可能要求被响应,但对请求的处理是通过插件机制拓展的,且对每个请求的处理都是一个链条,存在处理、加工、再处理的逻辑关系。仓库 前沿技术/20.精读《Nestjs》文档.md 也指出:Nestjs 基于 Express 封装、以 Modules 组织应用,其中间件实现与 Modules 完美结合——中间件组成的处理链正是职责链模式的工程化体现。

结构图:一条可中断、可穿透的环路

原文档给出了职责链模式的标准结构(Handler → ConcreteHandler):

  • Handler就是对请求的处理,可以看到这里是一条环路:只要处理完之后就可以交给下一个 Handler 进行处理,可以在中途拦截后中断,也可以穿透整条链路。
  • ConcreteHandler是具体 Handler 的实现,它们都需要继承 Handler 以具备相同的HandleRequest方法,这样每一个处理中间件就都拥有了处理能力,使得这些对象连成的链条可以对请求进行传递。

代码例子:两种职责链实现方式

职责链实现方式非常多,比如 Koa 的洋葱模型实现原理就值得再写一篇文章(可参考 co 源码 理解其背后的 Generator/Promise 调度)。这里仅介绍最简单场景的实现方案。

职责链的简单实现模式也分为两种:

  1. 每个对象本身维护到下一个对象的引用;
  2. 由 Handler 统一维护后继者。

下面例子使用 typescript 编写,采用"每个 Handler 持有后继引用"的方式:

public class Handler { private nextHandler: Handler public handle() { if(nextHandler) { nextHandler.handle() } } }

每个 Handler 的默认行为就是触发下一个链条的handle,因此什么都不做的话,这个链条是完全打通的,因此我们可以在链条的任何一环进行处理。

处理的方式就是重写handle函数:

  • 重写时维持对nextHandler.handle()的调用 → 链条继续向后传递(穿透);
  • 重写时不调用 → 链条在此处终止(阻断)。

这与 JS 事件冒泡中的stopPropagation()、中间件机制中的"放行 / 不放行"是同一套思想:默认放行,显式阻断。

弊端:职责链的信任边界与适用边界

职责链模式并非银弹,有两个典型弊端:

其一,不保证每个中间件都有机会处理请求。因为中间件顺序的问题,后面中间件可能被前面的中间件阻断,因此当中间件之间存在不信任关系时,职责链模式并不能保证中间件调用的可靠性。也就是说,链上的"机会平等"只是结构上的平等,执行顺序上后置节点随时可能被前置节点"短路"。

其二,不要扩大设计模式的使用范围。对一堆对象的连续调用就没必要使用职责链模式,因为:

  • 职责链适合对象数量不确定、是否处理请求由每个对象灵活决定的场景;
  • 确定了对象数量以及是否调用的场景(顺序固定、逐一调用的管线),直接写循环或显式调用即可,引入职责链反而增加抽象成本。

总结:职责链与组合模式的组合

职责链模式是插件机制常用的设计模式,在事件机制、请求处理中有广泛的应用。本仓库的组件值校验(可视化搭建/275.组件值校验.md)正是通过中间件拓展校验规则的职责链实践;而中间件机制本身在 前沿技术/53.精读《插件化思维》.md 中也有独立论述,可以相互印证。

职责链模式还可以与组合模式组合使用:组合模式描述的是一种统一管理的树形结构(参见 设计模式/174.精读《设计模式 - Composite 组合模式》.md),每个节点都可以把自己的父节点作为后继节点。实际上 DOM 结构就是一种组合模式,事件冒泡就是在其基础上拓展的职责链模式——树给了节点"父级在哪里"的结构信息,职责链给了事件"沿父级向上传递"的流动规则,两者组合正是浏览器事件模型的设计内核。

原文档出处:设计模式/179.精读《设计模式 - Chain of Responsibility 职责链模式》.md,属于本仓库"设计模式"基础模块的一部分(可查看 readme.md 中"设计模式"目录)。本文作为其扩展解读,核心观点与代码示例均继承自原文档,并结合仓库内中间件校验、组合模式等源码级案例进行佐证。

  • 文档
  • 技术博客
  • 教程

【免费下载链接】weekly

前端精读周刊。帮你理解最前沿、实用的技术。

项目地址:https://gitcode.com/GitHub_Trending/we/weekly
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

低清视频能修复吗,2026年画质修复工作流,5款工具怎么选

低清视频能修复吗?对于短视频矩阵运营和知识博主来说,素材模糊、暗光噪点多是直接影响完播率的硬伤。AI 画质修复是通过深度学习模型对低分辨率或受损视频进行超分重建与降噪处理的技术。在实际工程落地中,像鲸剪 WhaleClip 这类桌面端工具已…

作者头像 李华