Webpack 打包代码与模块提取
本节目标
- 看懂 Webpack 打包后「模块数组 + 启动函数」的结构
- 会定位「某个功能在哪个模块」
- 能把目标模块抽出来独立运行/分析
Webpack 把很多文件打成一个 JS,结构是「一个超大的模块数组 + 一个 __webpack_require__ 加载器」。
// 运行环境:Node.js 16+(无需依赖)
// 运行:node jsr-l13.cjs
// 简化的 webpack 产物结构:
const modules = {
0: function (module, exports, require) { module.exports = function add(a, b) { return a + b } },
1: function (module, exports, require) { const add = require(0); module.exports = add(1, 2) },
}
function __webpack_require__(id) {
const m = { exports: {} }
modules[id](m, m.exports, __webpack_require__)
return m.exports
}
console.log(__webpack_require__(1)) // 3逆向要点:
- 在打包文件里搜
modules/__webpack_require__确认是 webpack。 - 目标算法通常在「某个模块函数」里。定位方法:搜索算法特征字符串(如
createCipheriv),它会落在某个模块内。 - 抽离:把那个模块函数复制出来,补上它
require的依赖(或 mock),就能单独跑、单独分析。
名词解释
Webpack 模块数组:Webpack 把每个源文件编译成一个「函数」,所有函数塞进一个大对象(或数组),用数字 id 索引。运行时由 __webpack_require__(id) 按需执行。所以你看到的「一个巨大 JS」其实是「很多小模块的集装箱」。逆向要做的,是「在这个集装箱里找到装算法的那个格子」。
模块提取(Module Extraction):把「包含目标算法的那个模块函数」从打包文件里抠出来,单独运行分析。因为模块之间靠 require(id) 关联,你抽出后只需把被依赖的模块也一并补上(或简单 mock 掉无关依赖),就能脱离整个包单独调试算法——这是逆向 webpack 产物的核心技法。
课后练习
练习 1:怎么判断一个 JS 是不是 Webpack 打包的?
答案:搜
__webpack_require__、webpackJsonp、modules等关键字。出现function __webpack_require__(id)几乎可以确定。现代还可能用 Vite/Rollup,结构不同(Rollup 常用createCommonjsRequire之类),但「多模块合一」的本质类似。
练习 2:抽离模块时,若目标模块 require(5) 但 5 号模块是无关 UI,怎么处理?
答案:可以直接把
require(5)的调用结果 mock 成一个空对象/桩函数,只要不影响你要的算法执行即可。只保留算法真正依赖的模块,其余剪枝,才能让抽离的代码最小、最可读。
总结
Webpack 打包让「找算法」变成「集装箱里找格子」,但结构非常规律,反而好办。我的观点:别被那个几万行的单文件吓到——它只是「很多小模块被胶水粘在一起」,你真正要的通常只是其中「一个模块函数」。定位靠「搜算法特征字符串」(如 createCipheriv、md5),一旦命中就锁定模块 id;抽离时用 require 依赖做剪枝,把无关 UI 模块 mock 掉,目标算法立刻清爽。一句话:webpack 的产物「看起来吓人、拆开很简单」,关键是敢下手抽模块,而不是通读整包。