构建工具拦截 require 本质是在模块解析或源码转换阶段劫持 CommonJS 加载流程,而非运行时;Webpack 用插件/Loader/Parser 钩子,Rollup 借助 resolveId/load 钩子或改造 CommonJS 插件,通用推荐 AST 级重写,但动态 require 无法完全静态分析。

在构建插件(如 Webpack、Rollup 或自定义打包器)中拦截 require 调用,本质是劫持 CommonJS 模块加载的解析与执行流程。CommonJS 本身是运行时行为(Node.js 环境),而构建工具在编译期处理模块依赖,因此“拦截 require”实际发生在**模块解析(resolve)或源码转换(transform)阶段**,而非真正运行时。
Webpack 中通过 Plugin 拦截 require 调用
Webpack 不直接暴露对 require() 字符串调用的细粒度拦截,但可通过以下方式实现等效效果:
-
使用
NormalModuleReplacementPlugin:匹配require()解析路径,重定向到代理模块(适合按路径规则拦截) -
编写 loader(如
require-inject-loader):在模块源码被解析前,用正则或 AST 修改所有require('xxx')表达式(例如注入日志、替换路径、条件跳过) -
利用
compilation.hooks.succeedModule或javascript.JavascriptParser钩子(Webpack 5+):监听模块解析后的 AST,捕获CallExpression中的require调用并修改其参数或返回值
Rollup 中通过 Plugin 的 resolveId 和 load 拦截
Rollup 是基于 ES 模块设计的,但支持 CommonJS 插件(@rollup/plugin-commonjs)。要拦截 require,关键在于:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
-
resolveId钩子:当插件检测到require('xxx')触发的依赖解析时,可拦截并重写目标 ID(如将require('./utils')改为./utils.mock) -
load钩子配合transform:对含require的文件做源码重写(需启用commonjs插件前处理),例如用acorn解析 AST 并替换require调用 -
自定义 CommonJS 转换逻辑:fork 或 wrap
@rollup/plugin-commonjs,在其内部解析require语句时插入拦截逻辑(较深入,需理解其 ast-to-esm 转换流程)
通用方案:AST 级源码重写(推荐)
无论用哪种构建工具,最可靠的方式是操作 AST:
- 用
acorn或@babel/parser解析模块代码为 AST - 遍历
CallExpression节点,筛选callee.name === 'require' - 检查
arguments[0].value(即 require 的模块路径),按需修改、跳过、抛错或包裹(如__intercepted_require('x')) - 用
recast或@babel/generator生成新代码并返回给构建流程
注意边界与限制
CommonJS 的 require 是动态的,构建时无法 100% 静态分析所有情况(如 require(varName)、eval('require(...)'))。因此:
- 静态字符串路径(
require('./a'))可完全拦截;动态拼接路径需保守处理或报警告 - 不能真正“阻止 Node.js 运行时 require”,构建拦截只影响打包产物中的模块引用关系
- 若目标是调试/监控,建议结合运行时代理(如重写
Module._load)而非仅构建期
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










