Webpack 配置速查表 - 前端构建工具常用配置大全

Webpack 的能力是真实的,而这份能力要么在配置里成全你、要么在配置里藏起来。本表覆盖入口/输出、各类资源的 Loader、插件、dev-server/HMR、代码分割与打包优化、mode/environment。新资源类型加载不上、HMR 失效、或 bundle 莫名巨大时最有用。不会只丢给你一段样板配置,而是指到能解决问题的那个 flag 或插件。读完能在不啃完整文档的前提下配置并调优一次构建。

编程语言·共 45 条命令·最后更新 2026-07-21
webpack构建打包前端

典型使用场景

面向需要把前端工程化从零搭起来或接手已有构建配置的开发者。webpack 的难点不在命令本身,而在配置之间的耦合:entry 决定产物粒度,output.publicPath 决定运行时去哪儿加载 chunk,splitChunks 与 runtimeChunk 共同决定缓存命中率和首屏体积,devServer.proxy 又决定本地开发时请求能不能打到后端。本表按「入口与输出 → Loader → Plugin → 开发服务器 → 优化 → 模式与环境 → 构建分析」把这七个环节的常用配置排好序,每条都配了可直接粘贴的示例,并在易错点里集中了 contenthash 反复变化、Loader 顺序反了、Tree Shaking 不生效、代理写错路径这几类高频问题。

入口与输出 6

entry: "./src/index.js"
单入口文件
entry: { app: "./src/app.js", vendor: "./src/vendor.js" }
多入口配置
output.filename: "[name].[contenthash].js"
输出文件名(带内容 hash)
output.path: path.resolve(__dirname, "dist")
输出目录(绝对路径)
output.publicPath: "/assets/"
资源公共路径前缀
output.clean: true
构建前清理 dist(Webpack 5 内置)

Loader 配置 8

{ test: /\.css$/, use: ["style-loader", "css-loader"] }
CSS 内联到 DOM
{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"] }
CSS 抽离 + PostCSS
{ test: /\.js$/, exclude: /node_modules/, use: "babel-loader" }
Babel 转译 JS
{ test: /\.tsx?$/, use: "ts-loader" }
TypeScript 处理
{ test: /\.(png|jpg|gif)$/, type: "asset/resource" }
图片资源(Webpack 5 asset)
{ test: /\.svg$/, type: "asset/source" }
SVG 以字符串内联
{ test: /\.(woff2?|ttf|eot)$/, type: "asset/resource" }
字体资源
{ test: /\.(js|ts)$/, enforce: "pre", use: "source-map-loader" }
加载已有 source map

Plugin 插件 7

new HtmlWebpackPlugin({ template: "./src/index.html" })
自动生成 HTML 文件
new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" })
CSS 提取为独立文件
new DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") })
注入全局环境变量
new CopyWebpackPlugin({ patterns: [{ from: "public", to: "." }] })
复制静态文件到 dist
new ProvidePlugin({ _: "lodash" })
自动加载模块(无需 import)
new HotModuleReplacementPlugin()
HMR 热模块替换插件
new BannerPlugin({ banner: "Built at " + new Date().toISOString() })
给输出文件添加头部注释

开发服务器 6

devServer.hot: true
开启 HMR 热模块替换
devServer.historyApiFallback: true
SPA history 路由 404 回退到 index
devServer.port: 5000, open: true
指定端口并自动打开浏览器
devServer.static: path.join(__dirname, "public")
静态资源服务目录
devServer.proxy: { "/api": "http://localhost:3000" }
接口代理到后端服务
devServer.proxy: { "/api": { target: "http://localhost:3000", pathRewrite: { "^/api": "" } } }
代理并重写路径

优化配置 7

optimization.splitChunks: { chunks: "all" }
自动分割公共代码
optimization.minimize: true
开启代码压缩
optimization.minimizer: [new TerserPlugin({ extractComments: false })]
自定义 JS 压缩器
optimization.runtimeChunk: "single"
抽离 runtime 到单独文件
import("./module").then(m => m.default)
动态导入(路由懒加载)
/* webpackChunkName: "vendor" */
魔法注释命名 chunk
/* webpackPrefetch: true */
预取懒加载资源

模式与环境 6

mode: "production" | "development" | "none"
三种内置模式
webpack --config webpack.prod.js --mode production
CLI 指定配置与模式
module.exports = (env, argv) => ({ mode: argv.mode })
函数式配置读取 argv
devtool: argv.mode === "development" ? "eval-cheap-source-map" : "source-map"
按模式切换 source map
const { merge } = require("webpack-merge"); merge(common, prodConfig)
合并通用与环境配置
new webpack.EnvironmentPlugin(["NODE_ENV", "API_URL"])
批量注入 process.env 变量

构建分析 5

new BundleAnalyzerPlugin({ analyzerMode: "static" })
生成静态体积分析报告
stats: "minimal" | "normal" | "detailed"
控制构建输出日志详略
performance: { hints: "warning", maxAssetSize: 250000 }
资源过大性能提示
webpack --json > stats.json
导出构建统计 JSON(可上传分析)
devtool: "source-map" | "eval-cheap-module-source-map"
source map 速度与质量权衡

参数矩阵

参数作用示例
entry指定打包入口,可写字符串或映射为多入口entry: { app: "./src/app.js", vendor: "./src/vendor.js" }
output.filename: "[name].[contenthash].js"输出文件名带内容 hash,利于长期缓存output.filename: "[name].[contenthash].js"
output.path输出目录,需绝对路径output.path: path.resolve(__dirname, "dist")
output.publicPath运行时加载 chunk/资源的公共路径前缀output.publicPath: "/assets/"
output.clean: true构建前自动清理 dist 目录(Webpack 5 内置)output.clean: true
mode内置模式:production / development / nonemode: "production"
devtool控制 source map 类型,影响构建速度与调试质量devtool: "source-map"
{ test: /\.css$/, use: [...] }按扩展名匹配并指定 Loader 链{ test: /\.css$/, use: ["style-loader", "css-loader"] }
MiniCssExtractPlugin.loader把 CSS 抽离为独立文件而非内联注入{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"] }
exclude: /node_modules/排除不处理的目录,跳过第三方代码{ test: /\.js$/, exclude: /node_modules/, use: "babel-loader" }
type: "asset/resource"Webpack 5 资源模块:输出为独立文件并返回 URL{ test: /\.(png|jpg|gif)$/, type: "asset/resource" }
type: "asset/source"资源模块:把文件内容作为字符串内联{ test: /\.svg$/, type: "asset/source" }
enforce: "pre"把 Loader 提前到普通 Loader 之前执行{ test: /\.(js|ts)$/, enforce: "pre", use: "source-map-loader" }
HtmlWebpackPlugin({ template })按模板自动生成 HTML 并注入产物引用new HtmlWebpackPlugin({ template: "./src/index.html" })
MiniCssExtractPlugin({ filename })配合抽离 Loader 输出带 hash 的 CSS 文件new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" })
DefinePlugin把常量在编译期替换进产物,用于注入环境判断new DefinePlugin({ ... })
EnvironmentPlugin([...])批量把环境变量注入为全局常量new EnvironmentPlugin(["NODE_ENV", "API_URL"])
CopyWebpackPlugin({ patterns })把不需要编译的静态文件原样复制到产物目录new CopyWebpackPlugin({ patterns: [{ from: "public", to: "." }] })
ProvidePlugin自动注入模块引用,无需逐处 importnew ProvidePlugin({ _: "lodash" })
BannerPlugin({ banner })给输出文件加头部注释new BannerPlugin({ banner: "Built at " + new Date().toISOString() })
devServer.hot: true开启热模块替换,改代码不刷新页面devServer.hot: true
devServer.historyApiFallback: trueSPA history 路由 404 时回退到 index.htmldevServer.historyApiFallback: true
devServer.port / open指定开发服务器端口并自动打开浏览器devServer.port: 5000, open: true
devServer.static指定静态资源服务目录devServer.static: path.join(__dirname, "public")
devServer.proxy: string把前缀请求原样代理到后端devServer.proxy: { "/api": "http://localhost:3000" }
devServer.proxy: { target, pathRewrite }代理并重写请求路径devServer.proxy: { "/api": { target: "http://localhost:3000", pathRewrite: { "^/api": "" } } }
optimization.splitChunks: { chunks: "all" }自动把公共代码切成共享 chunkoptimization.splitChunks: { chunks: "all" }
optimization.runtimeChunk: "single"抽离运行时代码到单独文件,减少主包变更optimization.runtimeChunk: "single"
optimization.minimizer自定义压缩器配置optimization.minimizer: [new TerserPlugin({ extractComments: false })]
import("./module").then(...)动态导入,按需加载实现代码分割import("./module").then(m => m.default)
/* webpackChunkName: "vendor" */魔法注释给动态导入的 chunk 命名/* webpackChunkName: "vendor" */
/* webpackPrefetch: true */魔法注释在空闲时预取懒加载资源/* webpackPrefetch: true */
webpack-merge 的 merge()合并通用配置与环境配置的深拷贝结果const { merge } = require("webpack-merge"); merge(common, prodConfig)
function (env, argv) => ({...})函数式配置,可读取 argv.mode 做差异化module.exports = (env, argv) => ({ mode: argv.mode })
stats控制构建日志详略stats: "minimal"
performance: { hints, maxAssetSize }资源体积超限时给出性能提示performance: { hints: "warning", maxAssetSize: 250000 }
BundleAnalyzerPlugin({ analyzerMode })生成静态体积分析报告new BundleAnalyzerPlugin({ analyzerMode: "static" })

易错点与避坑指南

现象文件名里的 hash 每次构建都变,CDN 长期缓存完全失效。

原因contenthash 算的是模块内容,但产物里被注入了随构建变化的内容(如构建时间 Banner),或 contenthash 粒度配置让运行时模块变更牵动全部文件。

处置去掉随构建变化的注入内容,BannerPlugin 改用固定字符串或只在需要时启用;保持 output.runtimeChunk: "single" 隔离运行时。

现象样式写完页面完全不生效,且控制台无报错。

原因Loader 链执行顺序是从右到左,顺序写反了;或该扩展名没有任何 Loader 命中,CSS 被当成普通资源。

处置按「从右到左」检查 use 数组:css-loader 负责解析,style-loader 或 MiniCssExtractPlugin.loader 负责输出,两者位置不能颠倒。

现象生产构建里没用的函数仍然被打进包里,Tree Shaking 没生效。

原因代码用的是 CommonJS 的 module.exports / require,webpack 只能做静态分析,无法判定哪些导出未被使用。

处置把被摇树的模块改写成 ESM 的 import/export,production 模式才会真正裁掉未引用代码。

现象本地刷新页面正常,部署后路由页面白屏或资源 404。

原因output.publicPath 与实际部署路径不一致,运行时按错误前缀去加载 chunk 和静态资源。

处置把 publicPath 显式设为站点真实前缀(如 "/assets/"),并给动态导入的 chunk 用 /* webpackChunkName */ 固定命名便于定位。

现象本地开发时 /api/xxx 请求 404,而直接 curl 后端是通的。

原因devServer.proxy 没配或没覆盖该前缀,请求被 dev server 当作静态资源处理。

处置配置 devServer.proxy 并在需要时用 pathRewrite 去掉前缀:{ "/api": { target: "http://localhost:3000", pathRewrite: { "^/api": "" } } }。

现象主包体积只增不减,改一行业务代码后整个 JS 全部失效重下。

原因所有依赖都打进了同一个入口文件,缺少 splitChunks 与 runtimeChunk 拆分,任何改动都会改变主文件内容。

处置加 optimization.splitChunks: { chunks: "all" } 与 optimization.runtimeChunk: "single",把第三方库与运行时抽成长期稳定的独立文件。

现象dist 目录里堆着历次构建的旧文件,线上访问到了过期资源。

原因没有开启产物目录清理,旧 hash 文件一直残留。

处置开启 output.clean: true(Webpack 5 内置),构建前自动清空输出目录。

现象打包产物体积排查全靠猜,始终不知道哪个依赖最重。

原因构建日志用了 minimal 之类的精简档位,没有产物粒度的体积数据。

处置接入 new BundleAnalyzerPlugin({ analyzerMode: "static" }) 生成静态报告,或用 webpack --json > stats.json 导出统计后逐层下钻。

排障路径

  1. 1导出构建统计用于体积分析

    npx webpack --json > stats.json

    生成 stats.json 后可逐 chunk 查看模块构成,定位体积大头。

  2. 2生成静态体积分析报告

    new BundleAnalyzerPlugin({ analyzerMode: "static" })

    在 plugins 中加入后重新构建,会产出一份可离线打开的 HTML 报告。

  3. 3按模式分别指定配置与 source map 策略

    npx webpack --config webpack.prod.js --mode production

    控制台应输出 production 模式的构建摘要,devtool 走函数式配置里 argv.mode 的分支。

  4. 4打开精简构建日志快速看整体结果

    stats: "minimal"

    仅打印错误与警告及产物列表,构建结果一眼可见。

  5. 5开启资源体积告警定位超大产物

    performance: { hints: "warning", maxAssetSize: 250000 }

    超过 250000 字节的产物会在构建日志里给出 warning。

  6. 6核对运行时资源公共路径

    output.publicPath: "/assets/"

    部署后若资源 404,先确认这个前缀与站点真实路径一致,再检查 HTML 里的引用。

命令示例

生产配置骨架:contenthash + clean + HTML 插件

module.exports = { mode: "production", entry: { app: "./src/app.js" }, output: { path: path.resolve(__dirname, "dist"), filename: "[name].[contenthash].js", publicPath: "/assets/", clean: true, }, plugins: [new HtmlWebpackPlugin({ template: "./src/index.html" })], }

[contenthash] 只在文件内容变化时改变,配合 output.clean 清掉过期产物,才是长缓存策略的前提;publicPath 必须与部署路径一致,否则运行时所有资源 404。

CSS 抽离成独立文件 + 公共代码分割

module: { rules: [ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"] }, ], }, plugins: [new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" })], optimization: { splitChunks: { chunks: "all" }, runtimeChunk: "single", }

内联 style-loader 的 CSS 会被塞进 JS 并阻塞渲染,抽离后浏览器能并行下载 CSS 并提前解析;runtimeChunk 单抽一份,避免每个 chunk 重复带运行时。

devServer:SPA 回退 + 接口代理并去掉 /api 前缀

devServer: { port: 5000, hot: true, historyApiFallback: true, static: path.join(__dirname, "public"), proxy: { "/api": { target: "http://localhost:3000", pathRewrite: { "^/api": "" }, }, }, }

前端用相对路径 /api 调后端,同源问题一并消失;pathRewrite 决定后端看到的路径,前后端口不一致时少了这一步就是 404。historyApiFallback 负责刷新深层路由。

路由懒加载:动态导入 + 魔法注释命名与预取

const Settings = () => import(/* webpackChunkName: "settings" */ /* webpackPrefetch: true */ "./pages/Settings")

每个动态 import 会被打成独立 chunk,路由切换时才下载;webpackChunkName 让产物名可读,webpackPrefetch 在空闲时预取,首屏只付主包代价。

按模式切换 source map,导出 stats 供体积分析

module.exports = (env, argv) => ({ mode: argv.mode, devtool: argv.mode === "development" ? "eval-cheap-source-map" : "source-map", }) // CLI: webpack --config webpack.config.js --json > stats.json

开发用 eval-cheap-source-map 换取重建速度,生产用 source-map 换可定位的堆栈;两者取舍不同所以要按模式切换。stats JSON 可直接喂给体积分析工具找大模块。

常见坑与注意事项

  • output.filename 用 [hash] 而不是 [contenthash]:任一模块变化都会改掉整个 hash,用户缓存全部失效,回访等于重新下载整站。
  • devtool 用 "eval-*" 变体产出的代码必须靠 eval 执行;页面配了 CSP 且禁用 unsafe-eval 时整站脚本全挂,症状是白屏而不是构建报错,排查成本极高。
  • 生产把 source-map 直接部署到线上等于公开可还原的业务源码;正确做法是上传到错误监控平台并在配置里隐藏 sourceMap URL,或仅在内网保留。
  • 不写 mode 时 Webpack 默认为 production:本地调试会走压缩与 tree-shaking,报错堆栈是一行压缩后的代码,开发体验与线上表现同时失真。
  • output.publicPath 与实际部署路径不一致(挂在子目录、CDN 域名变更)时,构建成功但页面所有静态资源 404,首屏白屏且控制台只有资源加载错误。
  • DefinePlugin 注入整个 process.env 对象会连同 process 这个标识符一起被替换,依赖 process.env 下的 NODE_ENV 做环境分支的库(以及 polyfill 检测代码)会静默走错分支。

提示

  • Webpack 5 内置了 asset modules,替代了 file-loader 和 url-loader。
  • dev-server 的 hot: true 开启 HMR 热更新,开发体验更好。
  • splitChunks 可以按 vendor、common 等策略分割代码,减少首屏加载体积。
  • webpack-bundle-analyzer 可以可视化分析打包体积。
  • production 模式自动开启 Tree Shaking,需使用 ESM(import/export)才能生效。
  • [name].[contenthash].js 实现长期缓存,文件内容不变时 hash 不变,命中浏览器缓存。

常见问题

文件名里加 [contenthash] 到底解决了什么问题?

长期缓存。output.filename: "[name].[contenthash].js" 让产物名随内容变化:内容不变的 chunk hash 也不变,浏览器可以安心用强缓存;内容一变 hash 变、URL 变,用户自动拿到新文件,无需清缓存也不会拿到旧资源。相对地,固定名如 bundle.js 会让浏览器一直复用旧文件。配套设置有三项:output.path 必须给绝对路径(path.resolve(__dirname, "dist")),output.publicPath: "/assets/" 决定引用前缀,output.clean: true 让 Webpack 5 在构建前清空 dist,避免旧 hash 文件堆积成垃圾。CSS 侧同理用 MiniCssExtractPlugin 的 [name].[contenthash].css。

style-loader 和 MiniCssExtractPlugin.loader 什么时候换?

按「CSS 是运行时注入还是产出独立文件」选。开发期用 { test: /\.css$/, use: ["style-loader", "css-loader"] },style-loader 把 CSS 内联到 DOM,配合 devServer.hot: true 能做到样式热替换,改完立刻生效且不需要额外网络请求。生产期换成 { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"] },抽离成独立文件并接上 PostCSS,再用 new MiniCssExtractPlugin({ filename: "[name].[contenthash].css" }) 决定命名。判断依据很简单:内联方式无法利用浏览器缓存、首屏还会闪一下白,所以构建产物一律要抽离。

Webpack 5 里图片和字体怎么处理,还需要 file-loader 吗?

不需要,asset modules 已经内置并取代了 file-loader 和 url-loader。图片用 { test: /\.(png|jpg|gif)$/, type: "asset/resource" },产出独立文件并返回 URL;SVG 若要当字符串用用 { test: /\.svg$/, type: "asset/source" };字体同理 { test: /\.(woff2?|ttf|eot)$/, type: "asset/resource" }。另有一条 { test: /\.(js|ts)$/, enforce: "pre", use: "source-map-loader" },用 pre 阶段加载依赖包已有的 source map,这样压缩后的代码栈也能还原到原始 TS。静态目录整体搬运交给 new CopyWebpackPlugin({ patterns: [{ from: "public", to: "." }] })。

前端路由一刷新就 404,接口跨域又报错,怎么配 devServer?

两个问题分别对应两个选项。historyApiFallback: true 把 SPA 的 history 路由在找不到文件时回退到 index.html,于是直接访问 /users/123 也能由前端路由接管,刷新不再 404。跨域则用 proxy 转发:{ "/api": "http://localhost:3000" } 已能解决大部分;需要改路径时用对象形式 { "/api": { target: "http://localhost:3000", pathRewrite: { "^/api": "" } } },把前缀剥掉再发给后端,这样后端不用为开发环境加 /api 前缀。其它常用项:devServer.hot: true 开 HMR,devServer.port: 5000, open: true 指定端口并自动开浏览器,devServer.static 指向 public 目录。

首屏包太大,怎么拆包和懒加载?

四条配置一起上。optimization.splitChunks: { chunks: "all" } 自动把公共代码从各入口切出来;optimization.runtimeChunk: "single" 把 webpack runtime 抽成单独文件,避免每个 chunk 重复携带;optimization.minimize: true 开启压缩,需要自定义时用 optimization.minimizer: [new TerserPlugin({ extractComments: false })]。路由级懒加载靠动态导入 import("./module").then(m => m.default),配合魔法注释 /* webpackChunkName: "vendor" */ 命名 chunk、/* webpackPrefetch: true */ 在空闲时预取。生产模式自动开启 Tree Shaking,但只有用 ESM 的 import/export 才能真正生效。

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。

由 巧匠 维护

公开更新于 2026年7月21日,内容持续校对官方文档。

联系我们

命令或描述有误?提交反馈、商务合作或产品建议都可发送邮件给我们。

联系我们