
webpack 5 如何用 ManifestPlugin 生成构建产物清单并自定义过滤与生成逻辑【免费下载链接】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 5 项目构建后产出了多个 chunk、拷贝资源foo.txt之类的静态文件和 source map而你的部署脚本、CDN 同步任务或 SSR 服务端需要一份“这次构建到底输出了哪些文件”的清单ManifestPlugin就是 webpack 内置的解决方案它在每次编译结束时生成一份包含entrypoints各入口引入的文件和assets产物文件名到源文件/产物路径的映射的清单并通过filter、generate、serialize三个钩子函数让你自定义“哪些文件进清单、清单里再加什么字段、最终写成什么格式”。官方示例位于 examples/manifest-plugin本文基于该示例与 lib/ManifestPlugin.js 的实现整理。以下命令与配置均可在 webpack 5本仓库当前版本为 5.110.3下运行。准备示例的入口与产物构成官方示例的入口 example.js 同时覆盖了清单中常见的几类资产import fooURL from ./foo.txt; const barURL new URL(./bar.txt, import.meta.url); async function loadAsync() { return import(./async.js); } await loadAsync(); export default [fooURL, barURL];这里会产出静态文件foo.txt经file-loader处理、new URL引用的bar.txt、入口 chunkoutput.js、动态import(./async.js)产生的异步 chunk以及开启devtool: source-map后的 source map 文件。示例目录 examples/manifest-plugin 中还包含 async.js、foo.txt、bar.txt实际搭建时把这几个文件一并复制即可。清单中每一项的含义定义在 declarations/plugins/ManifestPlugin.d.tsassetsRecordstring, ManifestItem每项为{ file, src? }——file是产物路径经prefix处理后为绝对 URL 形式src是相对 context 的源文件路径entrypoints每个入口对应{ imports: string[], parents?: string[] }imports是该入口引入的文件名parents是父入口名存在时才会出现。基本用法在 plugins 中注册 ManifestPlugin示例配置 examples/manifest-plugin/webpack.config.js 同时注册了两个ManifestPlugin实例分别输出manifest.json和经过完全自定义的manifest.yml。第一个实例只传了filename用来展示默认行为use strict; // ts-expect-error no types for yamljs const YAML require(yamljs); const webpack require(webpack); // 示例仓库内写作 require(../../)独立项目中替换为 webpack /** type {import(webpack).Configuration} */ const config { devtool: source-map, output: { chunkFilename: [name].[contenthash].js }, optimization: { chunkIds: named // To keep filename consistent between different modes (for example building only) }, module: { rules: [ { test: /foo.txt/, use: require.resolve(file-loader) } ] }, plugins: [ new webpack.ManifestPlugin({ filename: manifest.json }), new webpack.ManifestPlugin({ filename: manifest.yml, prefix: /nested/[publicpath], filter(item) { if (/.map$/.test(item.file)) { return false; } return true; }, generate(manifest) { delete manifest.assets[manifest.json]; manifest.custom value; return manifest; }, serialize(manifest) { return YAML.stringify(manifest, 4); } }) ] }; module.exports config;运行构建需要webpack、file-loader和yamljs后者仅第二个实例的serialize用到仓库中该require带ts-expect-error no types for yamljs注释因为yamljs没有类型声明。在独立项目中安装这三个依赖后用webpack-cli按上述配置执行一次构建即可。默认值与行为在 lib/ManifestPlugin.js 中filename不传时输出到output.path下的manifest.jsonDEFAULT_FILENAMEprefix不传时使用[publicpath]DEFAULT_PREFIXserialize不传时用JSON.stringify(manifest, null, 2)。六个配置项filename、prefix、filter、generate、serialize、entrypointsoptions 的 JSON Schema 对全部字段给出了解释汇总如下选项类型作用依据 Schema 描述与实现filenamestring输出文件名默认manifest.json写入output.path目录prefixstring为清单中每个条目的file添加路径前缀其中[publicpath]占位符会被替换为output.publicPathpublicPath为auto时替换为/见 lib/ManifestPlugin.jsfilterfunction过滤进入清单的文件对每个条目{ file, src? }返回false即剔除generatefunction接收 manifest 对象、修改后返回用于增删字段serializefunction接收 manifest 对象返回最终写入文件的字符串entrypointsboolean开关entrypoints段落默认为开启lib/ManifestPlugin.jsfilter收到的是已经拼好prefix的条目对象示例里用它剔除所有.map结尾的 source map 文件。generate在filter之后、序列化之前执行示例里做了两件事删掉manifest.json自身第一个插件输出的清单文件也会出现在编译产物中第二个清单不需要记录它以及注入自定义字段custom: value——ManifestObject允许additionalProperties所以可以任意扩展。执行构建并核对输出示例文档中给出的dist/manifest.json如下文档示例输出其中.txt资产的 contenthash 文件名会随构建内容变化{ entrypoints: { main: { imports: [ main.js ] } }, assets: { foo.txt: { file: dist/3ee037f347c64cc372ad18857b0db91f.txt, src: foo.txt }, bar.txt: { file: dist/a0145fafc7fab801e574.txt, src: bar.txt }, output.js.map: { file: dist/output.js.map }, main.js: { file: dist/output.js }, async_js.js.map: { file: dist/async_js.0eeb6882e0cf674fd1fc.js.map }, async_js.js: { file: dist/async_js.0eeb6882e0cf674fd1fc.js } } }而应用了prefix/filter/generate/serialize的dist/manifest.yml同样是文档示例输出entrypoints: main: imports: - main.js assets: foo.txt: file: /nested/dist/3ee037f347c64cc372ad18857b0db91f.txt src: foo.txt bar.txt: file: /nested/dist/a0145fafc7fab801e574.txt src: bar.txt main.js: file: /nested/dist/output.js async_js.js: file: /nested/dist/async_js.0eeb6882e0cf674fd1fc.js custom: value逐项核对四个自定义点可以确认每个钩子都生效了prefix: /nested/[publicpath]——所有file值前多出了/nested/说明[publicpath]被解析成了配置中对应的publicPathfilter——output.js.map和async_js.js.map没有出现在assets里而 JSON 版本未过滤保留了它们generate——assets里没有manifest.json条目示例删除的是第一个清单文件名且顶层多了custom: valueserialize——输出是 YAML 而非 JSON缩进为 4 空格对应YAML.stringify(manifest, 4)。构建成功时webpack 的 stats 输出中会列出清单文件本身。示例文档展示了 Unoptimized 与 Production mode 两种情况的 stats文档示例输出两者都能看到类似这样的行说明清单文件已作为资产写入磁盘asset manifest.json 601 bytes [emitted] asset manifest.yml 395 bytes [emitted] webpack X.X.X compiled successfully也就是说验证方式就是构建成功后检查output.path下是否存在manifest.json/manifest.yml其内容结构符合上面的entrypointsassets形状。用清单还原入口的初始脚本与样式当清单用于给 HTML 模板或服务端渲染收集入口需要的资源时示例 README 还给出了一个importEntrypoints辅助函数按入口名递归解析entrypoints含parents指向的父入口并按.css后缀把导入分成样式与脚本两组const fs require(fs); function importEntrypoints(manifest, name) { const seen new Set(); function getImported(entrypoint) { const scripts []; const styles []; for (const item of entrypoint.imports) { const importer manifest.assets[item]; if (seen.has(item)) { continue; } seen.add(item); for (const parent of entrypoint.parents || []) { const [parentStyles, parentScripts] getImported(manifest.entrypoints[parent]) styles.push(...parentStyles); scripts.push(...parentScripts); } if (/\.css$/.test(importer.file)) { styles.push(importer.file); } else { scripts.push(importer.file); } } return [styles, scripts]; } return getImported(manifest.entrypoints[name]); } const manifest JSON.parse(fs.readFileSync(./manifest.json, utf8)); // Get all styles and scripts by entry name const [styles, scripts] importEntrypoints(manifest, main);注意仓库原文这里写作JSON.parser(fs.readFilsSync(...))是笔误上面已修正为JSON.parse和fs.readFileSync复制使用时以修正后的为准。边界与限制清单只在processAssets的PROCESS_ASSETS_STAGE_SUMMARIZE阶段生成lib/ManifestPlugin.js即所有编译产物就绪之后assets里记录的就是本次编译实际发出的文件。带hotModuleReplacement信息的资产和HotUpdateChunk会被跳过不出现在清单中lib/ManifestPlugin.js。assets的键名优先级是chunk 名 扩展名如main.js→ 资产的sourceFilename如foo.txt→ 从文件名中去掉 hash 的兜底值所以静态文件在清单中通常以源文件名作键并通过src字段指向源文件。示例配置中optimization.chunkIds: named的作用在源码注释中写明让不同 mode 下的文件名保持一致便于对照清单。进一步查看完整的输入输出对照直接读 examples/manifest-plugin/README.md选项定义参见 declarations/plugins/ManifestPlugin.d.ts 与 schemas/plugins/ManifestPlugin.json。【免费下载链接】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),仅供参考