拓冰建站拓冰建站
首页 / 资讯中心 / 正文

webpack 原生 ES Module 库打包实战:examples/module 全解(output.module / library.type: “module“)

webpack 原生 ES Module 库打包实战examples/module 全解output.module / library.type: 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本文基于 webpack 官方示例 examples/module讲解如何将一个多模块的 ES Module 应用通过配置experiments.outputModule、output.module与library.type: module打包成「既是可独立运行、又保留命名导出的原生 ESM 产物」并结合非优化development与生产production两份真实输出逐行剖析模块拼接scope hoisting、导出重命名、tree-shaking 与变量压缩在其中的具体表现。读完本文你将掌握用纯 ESM 格式发布 webpack 库/组件的完整配置、读懂产物中模块合并与导出信息的方法以及背后的源码实现入口。一、示例概览三个模块组成的微库examples/module是 webpack 仓库 examples 集合中专门演示“以 ES Module 形式输出代码output as module / library”的用例。它只有四个文件example.js入口负责组合逻辑并对外导出inc与printmethods.js工具模块re-export 计数器的reset并定义printcounter.js持有闭包内状态的“计数器”模块webpack.config.js本示例的核心配置了 ESM 输出与模块合并优化。目录中的 template.md 是示例的渲染模板用_{{example.js}}_这类占位符嵌入源码、产物与构建信息而 README.md 则是构建后生成的“成品文档”其中保留了本次编译的真实源码与真实dist/output.js是学习 webpack 输出形态的第一手资料。三个源文件的职责如下完整源码后续产物分析都围绕它们展开入口 example.jsimport { increment as inc, value } from ./counter; import { resetCounter, print } from ./methods; print(value); inc(); inc(); inc(); print(value); resetCounter(); print(value); export { inc, print };中间层 methods.jsexport { reset as resetCounter } from ./counter; export const print value console.log(value);状态模块 counter.jsexport let value 0; export function increment() { value; } export function decrement() { value--; } export function reset() { value 0; }运行逻辑很容易梳理初始value 0打印 0随后inc()连续三次让value变成 3打印 3再通过methods.js转导出的resetCounter即counter的reset把value归零最后打印 0。值得注意的是methods.js使用了export { reset as resetCounter } from ./counter这种**转导出re-export**写法它和example.js末尾的export { inc, print }一起共同决定了最终 ESM 产物对外暴露的 API 形状——这是理解本示例为什么“既是一个应用又是一个库”的关键。二、核心配置解读让 webpack 产出原生 ESM本示例真正特别的地方在 webpack.config.jsuse strict; /** type {import(webpack).Configuration} */ const config { output: { module: true, library: { type: module } }, optimization: { usedExports: true, concatenateModules: true }, experiments: { outputModule: true } }; module.exports config;四个配置项协同作用缺一不可逐个说明配置项作用前置条件/说明experiments.outputModule: true总开关。开启“输出 ES Module”这一实验性能力webpack 才会允许产物体裁使用import/export并允许output.module与library.type: module生效必须显式开启默认关闭output.module: true声明产物是原生 ES Module异步 chunk 通过import()加载代码依赖原生export不再注入webpackJsonp这类 IIFE 启动代码由experiments.outputModule解锁output.library.type: module以库library的语义输出——把入口模块的导出当作库的公共 API通过原生export保留对外接口属于 webpack 众多library.type之一需experiments.outputModuleoptimization.concatenateModules: true开启作用域提升scope hoisting / 模块拼接把互相依赖的模块内联到同一个作用域减少运行时开销、便于后续压缩与 ESM 输出天然契合optimization.usedExports: true标记“哪些导出被使用”为 tree-shaking 提供依据配合生产压缩可删除未使用导出从源码实现角度可以印证这几个配置的联动在 lib/config/normalization.js 与 lib/config/defaults.js 中outputModule这类实验开关会被规范化并下发给各插件而“module 类型库”的真正落地者是 lib/library/ModuleLibraryPlugin.js——它通过ConcatSource组织源码、借助HarmonyExportSpecifierDependency/HarmonyExportImportedSpecifierDependency等 Harmony 导出依赖处理命名导出并从ExportsInfo的UsageState判断各导出是否被外部使用决定哪些导出要被保留为库的公共 API。模块拼接能力则由 lib/optimize/ConcatenatedModule.js 承担它在源码首部依赖清单中同样出现。也就是说本示例是“ESM 输出 模块拼接 按需保留导出”三种机制叠加后的最小演示集。三、非优化产物逐段拆解一次“看得到模块”的打包在没有开启压缩的普通development构建下webpack 生成 dist/output.jsREADME 中展示775 bytes/*!********************************!*\ !*** ./example.js 2 modules ***! \********************************/ /*! namespace exports */ /*! export inc [provided] [used in main] [could be renamed] - ./counter.js .increment */ /*! export print [provided] [used in main] [could be renamed] - ./methods.js .print */ /*! runtime requirements: __webpack_exports__, __webpack_require__.d, __webpack_require__.* */ ;// ./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 };这份产物信息量极大值得逐段阅读文件头 banner 注释!*** ./example.js 2 modules ***!表明该 bundle 是一个拼接后的整体模块concatenated moduleoptimization.concatenateModules生效。导出注释块/*! namespace exports */表示采用“命名空间导出”语义export inc ... - ./counter.js .increment逐条记录了每个公共导出的来源路径——inc来自counter.js的incrementprint来自methods.js的print并标注[provided]模块提供了该导出、[used in main]在入口运行时被使用、[could be renamed]允许被重命名为压缩留白。runtime requirements注释声明该模块运行期需要的 webpack 运行时全局量如__webpack_exports__、__webpack_require__.d等对应 lib/RuntimeGlobals.js 中定义。由于产物是原生 ESM 且所有模块被拼接进同一作用域这里没有出现传统的 webpack 启动 runtime 代码。模块体按依赖拓扑内联counter.js→methods.js→example.js依次出现。模块间的import/export被“摊平”变成同一作用域内的普通函数与变量。例如methods.js的export { reset as resetCounter } from ./counter被消解为对counter.js中reset函数的直接引用并因重名冲突被重命名为counter_reset。共享状态的本质value在counter.js片段中声明为普通let value 0但由于三份源码被拼接在同一个作用域example.js片段里直接调用increment()、print(value)访问的就是同一份value—— 模块级状态的语义被原样保留这正是 scope hoisting 的正确性前提。末尾的export语句export { increment as inc, print };是整个拼接模块唯一的对外出口。inc是increment的别名导出print直接导出。凡是没出现在这个 export 里的如decrement、value、resetCounter都不会泄漏到产物外部——这是“模块即库”的边界。四、生产产物解析压缩、改名与 tree-shaking 的合力开启 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};与开发版产物对照可以清晰看到生产构建做了四件事导出按需保留usedExports library 语义注释里的[all exports used]、stats 中的[exports: inc, print]说明压缩器知道公共 API 只有inc与print因此只保留这两个导出未使用代码被删除tree-shakingcounter.js的decrement、methods.js中未被外部引用的resetCounter别名等均未出现在产物里——它们对库用户不可见且自身未被使用被判定为死代码剔除标识符全部混淆value→o、increment→n、print→c呼应开发版注释中的[could be renamed]微小函数内联counter_reset()的调用被替换为直接赋值o0reset 函数体被内联展开最终连函数调用都省去了。最值得留意的是末尾export{n as inc,c as print}——即便内部变量名已被压缩到极致对外导出的名字inc、print仍被精确保留。这正是library.type: module的意义库的公开 API 是稳定契约内部实现细节则可以随意优化。五、读懂构建信息Info / Stats从统计视角验证构建时 webpack 输出的统计信息README 中Info一节同样值得逐字段解读Unoptimized非优化构建asset 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 mode生产构建asset 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 (runtime: main) output.js (main) 453 bytes [entry] [rendered]只有一个名为main的入口 chunk453 bytes 为源码模块总大小[rendered]表示已渲染输出./example.js 2 modules再次印证模块被合并统计三个模块形成一个可优化单元[exports: inc, print]与[all exports used]webpack 静态分析得到入口的导出集合且这些导出全部被消费被当作库导出entry ./example.js main入口来源used as library export这是本示例区别于普通应用打包的关键标记——它说明入口导出被用作库的导出因此生产构建也不会把它们当作“未使用导出”删掉两种模式下 asset 体积差异775 → 110 bytes与[minimized]标记量化展示了压缩与 tree-shaking 的效果。六、如何构建与验证由于 README.md 本身是由 template.md 渲染生成的任何能构建 webpack 示例的环境都可以复现本文的全部产物。官方 examples 的通用构建步骤记录在 examples/README.md在仓库根目录运行yarn安装依赖在仓库根目录运行yarn setup完成示例环境准备在仓库根目录运行yarn add --dev webpack-cli安装 CLI进入具体示例目录如examples/module执行node build.js触发构建若想一键构建全部示例在根目录执行npm run build:examples。构建完成后examples/module目录下会生成dist/output.js以及渲染完毕的 README 统计信息。由于产物是标准的 ES Module验证方式也很直接在支持 ESM 的环境中直接import { inc, print } from ./output.js即可获得与counter.js/methods.js等价的 API——例如print(value)打印当前计数值反复调用inc()会因模块级value状态的共享而持续累加。这也说明了此类配置的典型使用场景把一个由多个 ES Module 组成的应用/组件库发布成不依赖打包器 runtime、可被浏览器script typemodule、Node.js ESM 或其他 ESM 打包器直接消费的产物。七、延伸如何结合仓库源码继续深挖如果你想从“看懂示例”进阶到“吃透实现”仓库中与本示例强相关的源码入口包括lib/library/ModuleLibraryPlugin.jslibrary.type: module的完整实现约 799 行。它组合使用ConcatSource、HarmonyExportExpressionDependency、HarmonyExportImportedSpecifierDependency与HarmonyExportSpecifierDependency处理各形态的导出并借助ExportsInfo的UsageState见 lib/ExportsInfo.js决定导出保留策略lib/optimize/ConcatenatedModule.js负责把./example.js 2 modules拼接为一个模块、消解 import/export 并做标识符重命名lib/config/normalization.js 与 lib/config/defaults.jsexperiments.outputModule等实验开关的规范化路径lib/RuntimeGlobals.js产物头部注释runtime requirements中__webpack_exports__、__webpack_require__.d等运行时全局量的定义出处官方渲染模板 template.md 与生成脚本 examples/template-common.js、examples/examples.js说明README.md/dist/是构建期产物任何修改示例源码后都应重新构建避免文档与代码脱节。若希望观察不同配置下的行为差异可将 webpack.config.js 与同目录族的 module-library同样输出 module 类型库但未开启usedExports对照编译对比两种optimization组合对产物导出信息的影响。小结examples/module用最小化的三个模块完整呈现了 webpack 输出原生 ES Module 库的全链路experiments.outputModule解锁能力、output.module声明体裁、library.type: module固定公共 API再叠加concatenateModules与usedExports获得“既极简、又不破坏对外契约”的产物。无论是想发布零依赖的组件库还是希望应用最终产物能被原生import直接加载这份 110 bytes 的示例产物都值得反复对照源码研读。【免费下载链接】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),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门