webpack bundle-loader 实战:用 Loader 把任意模块变成按需加载的异步 Chunk

发布时间:2026/9/7 8:26:07
webpack bundle-loader 实战:用 Loader 把任意模块变成按需加载的异步 Chunk webpack bundle-loader 实战用 Loader 把任意模块变成按需加载的异步 Chunk【免费下载链接】webpackA bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the application on demand. Through loaders, modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff.项目地址: https://gitcode.com/GitHub_Trending/web/webpack本篇以 webpack 官方示例code-splitting-bundle-loader为主线讲解Code Splitting代码分割与 Loaders加载器的组合玩法bundle-loader 会为file.js生成一个包装模块wrapper module该包装模块导出一个函数调用它即可异步获取内层模块的 exports。读完后你将理解如何用loader!file请求语法强制应用 loader、wrapper 内部的回调队列与__webpack_require__.e加载机制如何协作以及最终产物中 JSONP chunk 加载运行时的完整结构。示例定位Code Splitting 与 Loaders 的结合体该示例的文档开头明确指出This example combines Code Splitting and Loaders并建议读者先阅读单独演示这两项功能的示例——即 code-splitting原生动态import()切分与 loader自定义 loader 编写。与其他 code-splitting 示例不同这里异步加载并非由源码中的动态 import 触发而是由 loader 生成的包装代码触发。examples/README.md 中对该示例的描述是code-splitting-bundle-loader example demonstrating Code Splitting through the bundle loaderbundle-loader 已在仓库根目录 package.json 中声明bundle-loader: ^0.5.6属于示例专用依赖无需额外安装即可在本地构建。示例源码三个文件讲完核心机制example.js调用包装函数example.js 全文只有三行require(bundle-loader!./file.js)(function(fileJsExports) { console.log(fileJsExports); });这里有两个关键点bundle-loader!./file.js是 loader 请求语法!前缀强制对./file.js应用 bundle-loader。打包产物中该模块的标识会变为../../node_modules/bundle-loader/index.js!./file.js这一点可以在此示例的编译 stats 输出中直接验证见下文 Info 小节require(...)的返回值立即被当作函数调用bundle-loader 的作用正是让模块的module.exports不再是目标模块本身而是一个加载函数——传入回调异步拿回真正的 exports。file.js被包装的目标模块file.js 是一个最简单的 CommonJS 模块module.exports It works;运行效果就是异步打出It works但取值发生在一个独立的异步 chunk 中。webpack.config.js固定 chunk 文件名webpack.config.js 只配置了一项use strict; /** type {import(webpack).Configuration} */ const config { optimization: { chunkIds: named // To keep filename consistent between different modes (for example building only) } }; module.exports config;chunkIds: named使异步 chunk 以源文件名file_js作为 id保证在 development / production 等不同构建模式下输出文件名保持一致便于文档示例与调试。示例通过根目录的 examples/buildAll.js 统一执行cd example node build.js完成构建template.md即 template.md负责把构建产物注入生成 README.md。产物分析入口 chunk 中的包装模块构建后得到两个文件dist/output.jsmain 入口 chunk与dist/file_js.output.js异步 chunk。下文产物内容以仓库 README.md 中记录的实际构建结果为准。wrapper 模块源码回调队列 异步加载dist/output.js中由 bundle-loader 生成的模块模块 id 为 1是理解本示例的核心/*!***************************!*\ !*** ../../node_modules/bundle-loader/index.js!./file.js ***! \***************************/ var cbs [], data; module.exports function(cb) { if(cbs) cbs.push(cb); else cb(data); } __webpack_require__.e(/*! require.ensure */ file_js).then((function(require) { data __webpack_require__(/*! !!./file.js */ 2); var callbacks cbs; cbs null; for(var i 0, l callbacks.length; i l; i) { callbacksi; } }).bind(null, __webpack_require__))catch;逐段拆解module.exports function(cb) {...}实现了文档中wrapper module returns a function that can be called to asynchronously receive the inner module的语义若数据未就绪cbs存在把回调压入队列若 chunk 已加载完成cbs已被置为null立即同步回调data。这使多次调用方都能拿到结果且天然支持先调用后加载完成的竞态__webpack_require__.e(file_js)是 webpack 运行时的ensure chunk确保 chunk 加载函数注释require.ensure表明 bundle-loader 在早期 webpack 1.x 时代基于require.ensure实现如今等价于动态 import 的底层加载路径。它返回 Promisechunk 就绪后 resolve__webpack_require__(2)加载内层模块。注意其请求标注为!!./file.js——双!!是 webpack 的清空规则请求语法表示加载./file.js时不再应用任何 loader 规则得到的是未加工的原始模块对应产物中的模块 id 2.catch(__webpack_require__.oe)把加载失败如ChunkLoadError交给运行时统一的错误处理函数。chunk 加载运行时文件名规则与 JSONP 机制dist/output.js中还内嵌了完整的 chunk 加载运行时几个关键函数如下完整实现见 README.md 的dist/output.js小节/* webpack/runtime/ensure chunk */ __webpack_require__.e (chunkId) { return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) { __webpack_require__.fkey; return promises; }, [])); }; /* webpack/runtime/get javascript chunk filename */ __webpack_require__.u (chunkId) (chunkId .output.js);__webpack_require__.e聚合所有加载策略钩子__webpack_require__.f命名空间本示例中只有 JSONP 策略__webpack_require__.f.j__webpack_require__.u定义了异步 chunk 的文件名规则chunkId .output.js这正是产物中出现file_js.output.js的原因。JSONP 加载部分/* webpack/runtime/jsonp chunk loading */维护了installedChunks状态表——undefined表示未加载、null表示预加载/预取、[resolve, reject, Promise]表示加载中、0表示已加载加载失败会构造ChunkLoadError携带type、request、event字段供上层捕获。脚本注入由__webpack_require__.l完成创建script标签、120 秒超时保护setTimeout(..., 120000)、完成后从document.head移除节点。这些运行时函数由 webpack 的运行时模块体系生成源码位于 lib/runtime/ 目录与运行时全局标识符定义 lib/RuntimeGlobals.js 相对应。异步 chunkfile_js.output.js异步 chunk 的内容极为精简——仅包含目标模块本身(self[webpackChunk] self[webpackChunk] || []).push([[file_js],{ /***/ 2 /*!*****************!*\ !*** ./file.js ***! \*****************/ (module) { module.exports It works; /***/ } }]);它通过向全局webpackChunk数组 push[chunkIds, moreModules]数据来回注模块入口运行时劫持了该数组的push方法chunkLoadingGlobal.push webpackJsonpCallback.bind(...)push 即触发webpackJsonpCallback——把moreModules合入__webpack_require__.m再把对应 chunkId 标记为0已安装并 resolve 等待中的 Promise最终唤醒 wrapper 中的.then回调。编译 StatsUnoptimized vs ProductionUnoptimizeddevelopment 默认asset output.js 9.34 KiB [emitted] (name: main) asset file_js.output.js 348 bytes [emitted] chunk (runtime: main) file_js.output.js 28 bytes [rendered] ../../node_modules/bundle-loader/index.js!./file.js 7:0-14:2 ./file.js 28 bytes [built] [code generated] [used exports unknown] cjs self exports reference ./file.js 1:0-14 cjs require !!./file.js ../../node_modules/bundle-loader/index.js!./file.js 8:8-30 chunk (runtime: main) output.js (main) 375 bytes (javascript) 4.83 KiB (runtime) [entry] [rendered] ./example.js main runtime modules 4.83 KiB 6 modules dependent modules 281 bytes [dependent] 1 module ./example.js 94 bytes [built] [code generated] [used exports unknown] entry ./example.js main webpack compiled successfullystats 印证了前文的分析file_js异步 chunk 的引用来源是../../node_modules/bundle-loader/index.js!./file.jsloader 包装模块第 7–14 行内部以!!./file.js形式依赖原始模块——loader 链与清空规则请求一目了然。Production modeasset output.js 1.88 KiB [emitted] [minimized] (name: main) asset file_js.output.js 93 bytes [emitted] [minimized] chunk (runtime: main) file_js.output.js 28 bytes [rendered] ../../node_modules/bundle-loader/index.js!./file.js 7:0-14:2 ./file.js 28 bytes [built] [code generated] [used exports unknown] cjs self exports reference ./file.js 1:0-14 cjs require !!./file.js ../../node_modules/bundle-loader/index.js!./file.js 8:8-30 chunk (runtime: main) output.js (main) 375 bytes (javascript) 4.83 KiB (runtime) [entry] [rendered] ./example.js main runtime modules 4.83 KiB 6 modules dependent modules 281 bytes [dependent] 1 module ./example.js 94 bytes [built] [code generated] [no exports used] entry ./example.js main webpack compiled successfully生产模式下入口从 9.34 KiB 压缩至 1.88 KiB、异步 chunk 从 348 bytes 降至 93 bytes均带[minimized]标记模块标记由[used exports unknown]变为[no exports used]说明压缩阶段的副作用分析对 CommonJS 模块的 exports 使用判定更为确定。两组 stats 共同的4.83 KiB (runtime)/6 modules表明本例体积大头是 chunk 加载运行时6 个 runtime modules业务代码本身极小。原理小结与适用场景从源码结构与产物可以归纳出本示例的完整链路require(bundle-loader!./file.js)经 loader 请求语法生成包装模块其 exports 是一个带回调队列的加载函数包装模块顶层立即执行__webpack_require__.e(file_js)驱动运行时把file.js单独渲染为异步 chunkfile_js.output.js文件名由__webpack_require__.u规则决定运行时通过 JSONPwebpackChunk全局数组 脚本注入加载该 chunk、合入模块表resolve 后回调被依次触发内层模块以!!./file.js方式加载绕过其他 loader 规则保证拿到原始 CommonJS 模块。需要说明的是现代 webpack 已内置基于动态import()的原生代码分割见 examples/code-splitting 与 examples/code-splitting-harmonybundle-loader 这类第三方方案更多用于把无法改写源码的旧模块、或需要按调用时机加载的模块包装成异步单元的兼容场景。本示例的价值正在于展示了 webpack 的另一条能力面——loader 与 code splitting 机制的叠加loader 决定模块长什么样而__webpack_require__.e/ JSONP 运行时决定模块何时、如何到达两者正交且可自由组合。如何验证仓库已包含示例依赖无需安装进入示例目录执行构建cd examples/code-splitting-bundle-loader node build.js或运行根目录 examples/buildAll.js 批量构建全部示例对比dist/output.js中 wrapper 模块与dist/file_js.output.js的内容核对本篇所述模块 id1 wrapper、2 内层模块、回调队列逻辑与 JSONP 运行时代码交叉参考 examples/code-splitting-bundle-loader/README.md 中记录的 Unoptimized / Production 两组 stats确认 chunk 归属与体积符合预期。【免费下载链接】webpackA bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the application on demand. Through loaders, modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff.项目地址: https://gitcode.com/GitHub_Trending/web/webpack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考