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

资讯详情

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

webpack 原生 ES Module 输出实战:从 examples/module 剖析 output.module 库构建

webpack 原生 ES Module 输出实战:从 examples/module 剖析 output.module 库构建 webpack 原生 ES Module 输出实战从 examples/module 剖析 output.module 库构建【免费下载链接】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导读本文以当前仓库的 examples/module 示例文档为主线完整讲解如何用 webpack 将多个 ES Module 源文件打包为单个可直接被现代浏览器/运行时以原生import消费的 ES 模块产物并在其中使用“重新导出、跨模块共享可变状态、库方式导出”等典型手法。读完本文你将掌握output.module、experiments.outputModule、output.library.type: module的配置关系与适用前提并通过 development/production 两套真实产物与构建统计信息理解 webpack 内部的作用域提升scope hoisting、导出分析usedExports和压缩优化究竟做了什么。1. 示例文档与源码的关系examples目录是 webpack 官方仓库自带的“最小可运行教学单元”每个子目录通常由一组源文件、一份 template.md或直接是渲染后的 README.md与一份webpack.config.js组成。其中 examples/module/template.md 是生成最终 README 的文档骨架文件内使用_{{example.js}}_这类占位符由示例构建工具链在执行时把真实源码内容、真实构建产物与真实 stats 回填进去最终渲染为可供阅读的 examples/module/README.md。因此本文以渲染后的 examples/module/README.md 与目录中的真实源码、配置为准展开仓库根目录的 examples/buildAll.js 即批量驱动这些示例执行的入口脚本。examples/module目录只包含以下五个文件example.js入口模块entry同时充当“库入口”methods.js对counter的二次导出 工具函数counter.js维护可变状态并暴露增、减、重置方法的模块webpack.config.js让产物成为原生 ES 模块的核心配置README.md / template.md本文所述的配套说明文档。2. 三个源文件ES Module 之间如何协作整个示例的逻辑规模很小却精准覆盖了模块化的三个关键形态。2.1 counter.js模块级可变状态counter.js 内部维护一个模块作用域变量export let value 0; export function increment() { value; } export function decrement() { value--; } export function reset() { value 0; }关键点value是export let它允许其他模块通过“引用共享”读取最新值而decrement在本例中虽被导出却从未被使用——这正好为后文讲解usedExports的“未被引用的导出不会进入产物”埋下伏笔。2.2 methods.js命名重导出的中间层methods.js 演示了 ES Module 的**重导出re-export**语法export { reset as resetCounter } from ./counter; export const print value console.log(value);它把counter.js的reset改名成resetCounter重新导出同时自身定义print。这种“门面模块”写法在大型应用中用于收敛内部 API、防止深层路径直接泄漏。2.3 example.js入口先消费再外放example.js 既是 webpack 的入口也通过具名导出把自己变成“可被外部 import 的库”import { increment as inc, value } from ./counter; import { resetCounter, print } from ./methods; print(value); inc(); inc(); inc(); print(value); resetCounter(); print(value); export { inc, print };它演示了三条链路直接读取并修改counter的状态、经由methods间接重置状态、以及把inc与print作为库对外导出。由于模块级value在多次调用间持续累积输出依次为0、3、0。3. webpack.config.js让产物成为原生 ES 模块3.1 完整配置与注释examples/module/webpack.config.js 是全文的灵魂配置use strict; /** type {import(webpack).Configuration} */ const config { output: { module: true, library: { type: module } }, optimization: { usedExports: true, concatenateModules: true }, experiments: { outputModule: true } }; module.exports config;3.2 三个关键开关的源码级关系从配置校验与默认值代码可以看出三者必须配合使用experiments.outputModule: true开关总闸它是实验性特性的“总开关”。在 lib/config/defaults.js 中可以看到该选项会按 target 自动默认开启D(experiments, outputModule, universal || deno || bun)即 target 为 universal、deno、bun 时为true而一旦开启运行时代码与产物将按“ES 模块环境”来生成lib/WebpackOptionsApply.js 中据此选择module-import模块类型等。output.module: true开启 ESM 输出表示产物使用import/export语法输出。默认值同样由实验开关推导见 lib/config/defaults.jsF(output, module, () Boolean(outputModule))。同时在 lib/WebpackOptionsApply.js 中有明确校验——只有在experiments.outputModule开启时output.module: true、library.type: module等才被允许否则 webpack 会直接抛出配置校验错误output.module: true is only allowed when experiments.outputModule is enabledlibrary type module is only allowed when experiments.outputModule is enabledoutput.library.type: module决定如何暴露库声明最终产物是一个“以模块方式暴露导出”的库——即 bundle 末尾会把入口模块的export { inc, print }原样保留为整个文件对外部消费方暴露的具名导出这也是浏览器script typemodule与 ESM 打包工具能够直接import { inc } from output.js的原因。3.3 optimization 两项优化为何被显式打开usedExports: true启用导出使用情况分析。webpack 会标记每个导出是否被引用未被使用的导出如本示例的decrement将不会被打包进产物。它默认只在 production 模式打开lib/config/defaults.js 中D(optimization, usedExports, production)此处显式开启是为了让 development 构建也体现该行为。concatenateModules: true启用模块作用域提升 / scope hoisting把相互内联安全的多模块合并进同一个函数作用域减少运行时开销。它同样默认仅 production 开启lib/config/defaults.js。对应的插件是 lib/optimize/ModuleConcatenationPlugin.js在 lib/WebpackOptionsApply.js 中按该开关注册。适用前提提醒output.module产物面向原生支持 ESM 的环境现代浏览器、Deno、Bun或 Node 的.mjs/type:module场景如果需要兼容旧浏览器或 CJS 生态应改用library.type: commonjs2或umd等不要开启output.module。4. 产物逐行解读development未压缩输出示例文档展示的dist/output.jsdevelopment、未压缩775 bytes是被concatenateModules深度合并后的结果。剥离文件头注释后产物核心如下;// ./counter.js let value 0; function increment() { value; } function decrement() { value--; } function counter_reset() { value 0; } ;// ./methods.js const print value console.log(value); ;// ./example.js print(value); increment(); increment(); increment(); print(value); counter_reset(); print(value); export { increment as inc, print };这份产物可以提炼出四个值得记住的实现事实模块边界被“拍平”三个文件被拼进同一作用域没有__webpack_require__包装函数模块间依靠提升后的函数/变量直接互相引用——这就是 scope hoisting 的直接体现。产物头部的块注释如./example.js 2 modules说明这是把 1 个入口与 2 个伴随模块合并出的一个 chunk。命名冲突通过改写解决counter.js的reset在合并作用域中被改名为counter_reset()example.js里原属于methods.js重导出链上的resetCounter调用也被内联成了对counter_reset()的直接调用证明重导出在合并后已被消除、链路完全扁平化。decrement确实消失了虽然counter.js声明并导出了decrement但它在整个依赖图里无人引用usedExports分析将其剔除——对应产物顶部的导出注解export decrement [not provided]未提供。入口导出被原样保留文件末尾的export { increment as inc, print };正是output.library.type: module的工作成果——开发模式下命名都保持可读inc、print、counter_reset产物块注释中还能看到 webpack 对每个导出可读性的分析[provided] [used in main] [could be renamed]。5. 产物逐行解读production压缩输出同一份源码在 production 下被压缩后只剩110 byteslet o0;function n(){o}const coconsole.log(o);c(o),n(),n(),n(),c(o),o0,c(o);export{n as inc,c as print};对照 development 产物可以看清压缩与 tree shaking 的边界局部标识符全部被压缩为单字母value→o、increment→n、print→cvalue的累加、打印、重置这些必须执行的顶层副作用语句被原样保留但改成了紧凑的逗号表达式序列export { n as inc, c as print }是唯一不能省略的语法——因为库对外契约要求保留inc、print两个具名导出所以哪怕调用链再短n与c这两个声明也必须留下被导出。这正是“库模式下导出成为优化边界”的典型例证普通应用的未使用导出会被整棵删除而库的公开导出是硬性 API必须存活。6. 构建统计Info解读README 同时给出了两种模式下的构建统计可以对照产物验证优化效果Unoptimizeddevelopmentasset output.js 775 bytes [emitted] [javascript module] (name: main) chunk (runtime: main) output.js (main) 453 bytes [entry] [rendered] ./example.js main ./example.js 2 modules 453 bytes [built] [code generated] [exports: inc, print] [all exports used] entry ./example.js main used as library export webpack X.X.X compiled successfullyProduction modeasset output.js 110 bytes [emitted] [javascript module] [minimized] (name: main) chunk (runtime: main) output.js (main) 453 bytes [entry] [rendered] ./example.js main ./example.js 2 modules 453 bytes [built] [code generated] [exports: inc, print] [all exports used] entry ./example.js main used as library export webpack X.X.X compiled successfully两段输出传递了同一条重要信息chunk 的源码量都是453 bytes./example.js 2 modules即无论哪种模式webpack 都需要解析并处理这三个文件差异全部发生在产物阶段——development 产物 775 bytes、production 经压缩后 110 bytes相差约 86%。此外注意两点[javascript module]与[minimized]production 下产物被标记为“javascript module”且经过压缩[exports: inc, print]与[all exports used]说明入口模块的对外导出只有inc、print且因library.type: module的存在所有导出都被视为“已使用”used as library export——这正是库入口不会被 tree shaking 误删的保证与第 5 节看到的产物中export语句必须存活互为印证。7. 如何在本地运行与验证仓库的 examples 通常不需要额外脚手架即可复现。在仓库根目录即本仓库所在目录执行# 使用示例自带的 webpack 配置将 examples/module 的入口打包为 ES 模块产物 npx webpack --config examples/module/webpack.config.js构建完成后检查生成的dist/output.js即可看到与第 4、5 节一致的产物形态。若要观察两种模式的差异可分别为配置注入 mode例如--mode development与--mode production对比产物体积与是否出现export语句、以及decrement是否被 tree-shaking 移除。若想快速理解 scope hoisting 前后差异也可临时把配置中optimization.concatenateModules改为false重新构建产物会退回为带__webpack_require__包装的传统形态与合并形态形成直观对照。关于 examples 的整体机制仓库根目录的 examples/examples.js 维护示例清单examples/buildAll.js 会逐个进入目录执行node build.js用于 CI/文档生成对examples/module而言其核心教学价值就在于第 3 节那份 18 行配置与第 4、5 节的产物对照。8. 小结通过 examples/module 这一最小闭环可以完整看到“ESM 源码 → scope hoisting 合并 → usedExports 裁剪 → 库导出保留 → 压缩输出”的全过程关注点结论如何输出原生 ESM开启experiments.outputModule并同时设置output.module: true如何把 bundle 当库用设置output.library.type: module产物末尾保留具名export未使用导出去哪了usedExports分析将其标记并从产物删除本例decrement多模块合并成什么样concatenateModules拍平模块边界、改写冲突命名本例reset→counter_reset库导出为何能存活库模式将所有公开导出视为 used压缩阶段也无法删除硬性 API适用环境原生支持 ESM 的运行时/浏览器传统环境需换用 commonjs/umd 类库类型这套“开发模式读得懂、生产模式压得狠、导出契约不丢失”的组合拳正是 webpack 面向现代 ESM 生态输出库与应用的推荐形态。若需继续深入可对照阅读 lib/WebpackOptionsApply.js 中相关校验逻辑、lib/config/defaults.js 中的默认值推导以及 lib/optimize/ModuleConcatenationPlugin.js 的实现细节。【免费下载链接】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),仅供参考
返回列表