十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

webpack 手动指定 Chunk 名称:通过 import() 魔法注释与 require.ensure 参数控制代码分割

webpack 手动指定 Chunk 名称:通过 import() 魔法注释与 require.ensure 参数控制代码分割 webpack 手动指定 Chunk 名称通过 import() 魔法注释与 require.ensure 参数控制代码分割【免费下载链接】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 官方示例examples/code-splitting-specify-chunk-name展开完整讲解如何在import()动态导入和require.ensure()中显式指定 chunk 名称如chunk-foo、chunk-bar-baz把模块手动分离到独立 chunk。示例演示了三种指定方式并结合 webpack 源码解析webpackChunkName魔法注释与require.ensure()第三个字符串参数的解析链路读完你可以掌握 chunk 命名、命名与 chunk id 的关系以及用它合并/隔离异步 chunk 的实战技巧。示例场景与目标该示例的核心目标来自 READMEThis example illustrates how to specify the chunk name inrequire.ensure()andimport()to separate modules into separate chunks manually.即在require.ensure()和import()中指定 chunk 名称把不同模块手动拆入不同的 chunk。示例源码位于 example.js完整代码为import(./templates/foo /* webpackChunkName: chunk-foo */ ).then(function(foo) { console.log(foo:, foo); }) require.ensure([], function(require) { var foo require(./templates/foo); console.log(foo:, foo); }, chunk-foo1); var createContextVar r; import(./templates/ba createContextVar /* webpackChunkName: chunk-bar-baz */ ).then(function(bar) { console.log(bar:, bar); })这段代码覆盖了三种指定 chunk 名称的方式对静态字符串的import()使用/* webpackChunkName: ... */魔法注释对require.ensure()直接传入第三个字符串参数chunk-foo1作为 chunk 名对动态拼接路径的import()会生成 lazy context 上下文模块仍可用webpackChunkName注释为整个上下文组命名。被引用的模板模块 templates/foo.js、templates/bar.js、templates/baz.js 都是同一模式var foo foo; export default foo;构建配置见 webpack.config.js只有一项关键设置use strict; /** type {import(webpack).Configuration} */ const config { optimization: { chunkIds: deterministic // To keep filename consistent between different modes (for example building only) } }; module.exports config;chunkIds: deterministic保证不同构建模式development / production下 chunk id也就是输出文件名前缀的数字如45、994、792保持一致便于对比 stats 输出与验证产物稳定性。编译产物chunk 名称出现在哪里入口 chunk 中被保留的命名注释编译后dist/output.js中的异步调用被改写为__webpack_require__.e(chunkId)而chunk 名称以注释形式保留在产物中__webpack_require__.e(/*! import() | chunk-foo */ 45).then(() (__webpack_require__(/*! ./templates/foo */ 2))).then(function(foo) { console.log(foo:, foo); }) __webpack_require__.e(/*! require.ensure | chunk-foo1 */ 45).then((function(require) { var foo __webpack_require__(/*! ./templates/foo */ 2); console.log(foo:, foo); }).bind(null, __webpack_require__))catch; var createContextVar r; __webpack_require__(1)(./ba createContextVar).then(function(bar) { console.log(bar:, bar); })两个关键观察点/*! import() | chunk-foo */ 45与/*! require.ensure | chunk-foo1 */ 45注释记录了“来源语法 指定名称 实际 chunk id”。注意两者都指向同一个 chunk id45——因为import(./templates/foo)与require.ensure(...)加载的是同一个模块./templates/foowebpack 会将其合并进同一个 chunk该 chunk 最终采用了先注册的名称chunk-foo第三条语句被编译成__webpack_require__(1)(./ba createContextVar)动态路径无法静态求值webpack 把它变成了lazy context惰性上下文模块运行时按 map 查找真实 chunk。Lazy Context 模块中的命名信息产物中生成的上下文模块头部注释同样携带了chunkName/*!**************************************************************************************************!*\ !*** ./templates/ lazy ^\.\/ba.*$ referencedExports: chunkName: chunk-bar-baz namespace object ***! \**************************************************************************************************/ const map { ./bar: [3, [994]], ./bar.js: [3, [994]], ./baz: [4, [792]], ./baz.js: [4, [792]] }; function webpackAsyncContext(req) { ... const ids map[req], id ids[0]; return __webpack_require__.e(ids[1][0]).then(() (__webpack_require__(id))); }可以看到chunkName: chunk-bar-baz被写入模块标识且 map 中./bar与./baz分别落到 chunk id994与792——由于是 context 拆出的多个 chunk它们被统一归到chunk-bar-baz组下。Stats 输出Unoptimized 与 Production示例 README 同时记录了 developmentUnoptimized与 production 两种模式下的 stats核心差异是文件体积压缩后output.js从 11 KiB 降到 2.63 KiB而chunk 结构完全一致这正得益于上面chunkIds: deterministic的配置。Unoptimized 输出的关键部分asset output.js 11 KiB [emitted] (name: main) asset 792.output.js 846 bytes [emitted] (name: chunk-bar-baz2) asset 994.output.js 846 bytes [emitted] (name: chunk-bar-baz0) asset 45.output.js 845 bytes [emitted] (name: chunk-foo) chunk (runtime: main) 45.output.js (chunk-foo) 38 bytes [rendered] ./templates/foo ./example.js 1:0-62 ./example.js 5:0-8:16 chunk (runtime: main) 792.output.js (chunk-bar-baz2) 38 bytes [rendered] ./baz ./templates/ lazy ^\.\/ba.*$ referencedExports: chunkName: chunk-bar-baz namespace object ./bazProduction 模式asset output.js 2.63 KiB [emitted] [minimized] (name: main) asset 994.output.js 117 bytes [emitted] [minimized] (name: chunk-bar-baz0) asset 45.output.js 116 bytes [emitted] [minimized] (name: chunk-foo) asset 792.output.js 116 bytes [emitted] [minimized] (name: chunk-bar-baz2)从中可以读出几个重要事实输出文件名仍由 chunk id 决定45.output.js、994.output.js名称只体现在(name: chunk-foo)标记里。chunk 名称的作用在于可读性、[name]模板变量output.filename中可用[name].[chunkhash].js、以及按名匹配做资源提示/优化同一个chunk-bar-baz组名被拆成了chunk-bar-baz0与chunk-bar-baz2两个 chunk——当命名组内包含多个真实 chunk 时webpack 会追加下标区分context 拆分出的子模块标记为[optional]见./templates/baz.js 38 bytes [optional]因为动态路径下的具体文件是运行时才确定的可选模块。源码级解析chunk 名称如何被解析import() 的 webpackChunkName 魔法注释import()的魔法注释由 ImportParserPlugin 处理。解析流程在parseCommentOptions(expr.range)取出注释键值对后lib/dependencies/ImportParserPlugin.js#L365-L366对webpackChunkName的校验与赋值逻辑为lib/dependencies/ImportParserPlugin.js#L399-L410if (importOptions.webpackChunkName ! undefined) { if (typeof importOptions.webpackChunkName ! string) { parser.state.module.addWarning( new (getUnsupportedFeatureWarning())( \webpackChunkName\ expected a string, but received: ${importOptions.webpackChunkName}., parser.getLocation(comment) ) ); } else { chunkName importOptions.webpackChunkName; } }两个值得注意的实现细节webpackChunkName必须是字符串。写成数字、布尔等类型只会触发 warningexpected a string不会抛编译错误——命名静默失效排查产物命名时要留意这一点同一文件中还有相邻的兄弟魔法注释webpackIgnoreboolean、webpackMode、webpackPrefetch、webpackPreload等lib/dependencies/ImportParserPlugin.js#L386-L450解析方式一致类型不符时 addWarning 并忽略。require.ensure() 的字符串参数require.ensure()的命名由 RequireEnsureDependenciesBlockParserPlugin 处理chunk 名取自调用参数lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js#L30-L53let chunkName null; ... const chunkNameExpr parser.evaluateExpression(expr.arguments[3]); if (!chunkNameExpr.isString()) return; chunkName ... (chunkNameExpr.string); ... if (!errorExpression !chunkName) { const chunkNameExpr parser.evaluateExpression(expr.arguments[2]); if (!chunkNameExpr.isString()) return; chunkName ... (chunkNameExpr.string); }即4 参形式require.ensure(deps, callback, name, errorCb)时第 3 参是 chunk 名3 参形式本示例用法require.ensure([], fn, chunk-foo1)时第 2 参即第三个实参是 chunk 名参数必须能被evaluateExpression静态求值为字符串字面量。若传入变量等无法静态求值的表达式isString()不成立解析直接return——命名不生效甚至整条 require.ensure 依赖不被处理这也是为什么示例中用的是字符串字面量chunk-foo1而非变量。名称合并与最终 chunk 归属从产物行为可以推断多个异步入口指向同一模块图时本例import()与require.ensure()都加载./templates/foo它们会被并入同一个 chunkid45产物注释中分别保留chunk-foo与chunk-foo1两处来源标记但 chunk 只取一个最终名称stats 中为chunk-foo。这提示实践中的规则想让多个 import 归入同一 chunk就给它们同一个webpackChunkName想让某个 import 独占 chunk就用一个其他 import 没有用的名字。实战要点与常见坑合并小 chunk 的官方提示。webpack 内置的 TinyChunksWarning 在产生过多极小 chunk 时会建议“Giving several import() calls one webpackChunkName groups them into a single chunk”——即把多个import()指定为同一个webpackChunkName把它们合并成一个 chunk 减少请求数。这是该功能最常见的实际用途。魔法注释位置。/* webpackChunkName: ... */必须紧跟在import()的参数之后、右括号之前如示例中import(./templates/foo /* ... */ )且注释值需是合法 JSON 字符串parseCommentOptions解析失败时会产生Compilation error while processing magic comment警告lib/dependencies/ImportParserPlugin.js#L368-L377。chunk 名 ≠ 文件名。默认output.filename不含[name]时文件名由 chunk id 生成45.output.js。若希望名称直接体现为文件名需要在output.filename或optimization.splitChunks的cacheGroups[...].name配合下使用[name]占位本示例保持默认文件名策略仅用名称做可读性标注与分组。require.ensure 已属旧语法。require.ensure是 webpack 兼容旧打包器SeaJS/SystemJS 风格提供的 API新代码推荐import()但本例同时覆盖两者说明命名机制在两条路径上均可用。与splitChunks的分工。手动webpackChunkName控制的是“每个动态入口自身的 chunk 归属”而 optimization.splitChunks 等配置控制的是“chunk 之间的自动拆分与公共模块抽取”两者作用于不同阶段可按需组合。验证方式示例仓库提供两种验证路径直接阅读产物对照按上文“编译产物”一节检查__webpack_require__.e(/*! import() | chunk-foo */ 45)注释与 stats 中(name: chunk-foo)标记是否一一对应运行示例构建webpack 的 examples 统一由 examples/buildAll.js 构建examples/code-splitting-specify-chunk-name目录下含 template.md 模板README 中_{{dist/output.js}}_、_{{stdout}}_占位即由构建产物回填生成。构建后运行yarn build相关脚本会输出上文的 Unoptimized / Production 两段 stats可逐一核对 chunk 名称是否生效。总结webpackChunkName魔法注释与require.ensure()的字符串参数是 webpack 手动控制代码分割命名的两个入口前者覆盖import()含动态 context后者覆盖传统异步加载语法两者都要求静态可求值的字符串类型或求值不符时以 warning 或静默忽略处理。产物中的命名注释/*! import() | chunk-foo */与 stats 的(name: ...)标记是验证命名是否生效的最直接依据。【免费下载链接】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),仅供参考
返回列表