// 不自动运行:需要先 npm install,然后 npm run build // webpack 5 配置示例:loader、plugin、splitChunks、contenthash、tree shaking、持久化缓存。以官方文档为准:https://webpack.js.org/concepts/ import path from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); // 自定义 plugin:构建结束后打印每个产物的大小(plugin = 在编译生命周期的钩子上做事) class SizeReportPlugin { apply(compiler) { compiler.hooks.done.tap("SizeReportPlugin", (stats) => { const assets = stats.toJson({ all: false, assets: true }).assets ?? []; for (const a of assets.sort((x, y) => y.size - x.size)) { console.log(`[size] ${a.name.padEnd(36)} ${(a.size / 1024).toFixed(2)} KiB`); } }); } } export default (env, argv) => { const isProd = argv.mode === "production"; return { entry: { main: "./src/index.js", admin: "./src/admin.js" }, // 两个入口,共享 utils 和第三方代码 output: { path: path.resolve(__dirname, "dist"), filename: isProd ? "[name].[contenthash:8].js" : "[name].js", // 内容变了文件名才变,利于长期缓存 clean: true, }, module: { rules: [ // 自定义 loader:把 .md 文件转成导出字符串的 JS 模块(loader = 把某类文件转换成 JS 模块) { test: /\.md$/, use: [{ loader: path.resolve(__dirname, "loaders/md-loader.cjs"), options: { trim: true } }] }, ], }, optimization: { usedExports: true, // 标记未使用的导出,压缩时删除(tree shaking) runtimeChunk: "single", // webpack 运行时单独一个文件,避免业务代码变化影响 vendor 的哈希 splitChunks: { chunks: "all", cacheGroups: { shared: { name: "shared", minChunks: 2, minSize: 0, priority: 10 }, // 两个入口都用到的代码 }, }, }, cache: { type: "filesystem" }, // 持久化缓存:第二次构建明显变快 plugins: [new SizeReportPlugin()], devtool: isProd ? false : "eval-cheap-module-source-map", }; };