在 webpack 配置里,mode 是一个不起眼却极其重要的选项。它只有三个取值:development、production、none。本文聚焦 production——它是什么、为什么需要它、以及实际项目里该怎么做。
一、什么是生产模式
生产模式,就是通过 mode: 'production' 告诉 webpack:这份产物是给真实用户访问的,请在构建时尽可能做优化,而不是优先考虑构建速度。
module.exports = {
mode: 'production',
};
在 webpack 4 之前,并没有 mode 这个概念,这些优化需要手动一个个开启;从 webpack 4 起,production 把一整套面向线上的优化打包成了默认行为,开箱即用。
需要说明的是,production 不是一个「环境」,而是 webpack 的一组内置优化策略的开关。它不会替你部署、不会替你配置服务器,它只影响「代码被打包成什么样」。
二、为了什么:开发与生产的目标不同
开发和生产的诉求几乎是相反的:
| 开发(development) | 生产(production) | |
|---|---|---|
| 首要目标 | 构建快、易调试 | 体积小、加载快 |
| 代码压缩 | 否 | 是 |
| 无用代码剔除 | 否 | 是(Tree Shaking) |
| Source Map | 详细、利于定位 | 精简或关闭 |
process.env.NODE_ENV | 'development' | 'production' |
| 错误提示 | 尽量详细 | 尽量精简 |
如果生产环境也用开发配置,产物会又大又慢,还会暴露内部实现细节;反过来,用生产配置开发,每次构建又慢得让人抓狂。所以 mode 的意义,就是让同一套构建工具能服务两种截然不同的目标。
三、production 自动做了什么
设置 mode: 'production' 后,webpack 会自动启用以下能力:
- 代码压缩:启用
TerserPlugin,压缩混淆 JS,去掉注释和多余空白。 - Tree Shaking:基于 ESM 的静态分析,剔除没有被使用的导出代码(
usedExports+sideEffects)。 - Scope Hoisting:通过
ModuleConcatenationPlugin把多个模块合并到一个函数作用域中,减少包裹代码、提升运行速度。 - 定义
process.env.NODE_ENV:自动用DefinePlugin将其设为'production',React、Vue 等库据此移除开发期警告代码。 - 确定性 ID:模块和 chunk 的 id 采用
deterministic策略,保证多次构建结果稳定,利于长期缓存。 - 构建失败即停止输出:启用
NoEmitOnErrorsPlugin,有错误时不产出有问题的产物。 - 性能提示:对体积过大的资源给出警告(
performance.hints)。
这也是为什么很多优化「你什么都没写却生效了」——它们都藏在 production 的默认配置里。
Tree Shaking 的前提
Tree Shaking 依赖 ES Module(import / export),CommonJS 因为运行时才能确定依赖,无法被有效摇树。此外,需要在 package.json 里声明 "sideEffects": false(或列出有副作用的文件),webpack 才敢安全地删除未使用模块。
{
"sideEffects": ["*.css", "*.less"]
}
四、怎么做:在生产模式下的实践
1. 通过命令行或环境变量切换
{
"scripts": {
"dev": "webpack serve --mode development",
"build": "webpack --mode production"
}
}
2. 拆分配置文件
真实项目通常会拆成三份:公共、开发、生产,再用 webpack-merge 合并:
webpack.common.js
webpack.dev.js
webpack.prod.js
npm install webpack-merge --save-dev
webpack.prod.js:
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
devtool: 'hidden-source-map', // 生成 Source Map 但不暴露给用户
output: {
filename: 'js/[name].[contenthash:8].js',
chunkFilename: 'js/[name].[contenthash:8].chunk.js',
clean: true,
},
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
}),
],
optimization: {
minimize: true,
minimizer: ['...', new CssMinimizerPlugin()],
splitChunks: {
chunks: 'all',
},
runtimeChunk: 'single',
},
});
几个要点:
[contenthash]:内容变化才改文件名,配合长期缓存。devtool: 'hidden-source-map':既保留定位错误的能力,又不把源码 map 暴露给浏览器。splitChunks+runtimeChunk:拆分第三方依赖和运行时,提升缓存命中率。minimizer: ['...', ...]:'...'表示保留 webpack 默认的 JS 压缩器,再追加 CSS 压缩。
3. 在代码中读取环境变量
DefinePlugin 注入的 process.env.NODE_ENV 可以在业务代码中使用,用于条件分支,这些分支在生产构建时会被静态求值并剔除:
if (process.env.NODE_ENV !== 'production') {
console.log('开发期日志');
}
生产打包后,上面这段会被直接删掉。
4. 剔除开发专用配置
生产配置里不应包含:
devServer:这是给本地开发用的。- 过于详细的
devtool(如eval-cheap-module-source-map)。 - 开发期的 HMR、overlay 等。
5. 常见补充插件
npm install compression-webpack-plugin --save-dev
const CompressionWebpackPlugin = require('compression-webpack-plugin');
plugins: [
new CompressionWebpackPlugin({
algorithm: 'gzip',
threshold: 10240,
}),
],
它会在构建时生成 .gz 文件,配合服务器开启 gzip 静态压缩,进一步减小传输体积。此外,webpack-bundle-analyzer 可用于分析产物体积,找出过大依赖。
五、完整配置示例
const path = require('path');
const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
devtool: 'hidden-source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash:8].js',
chunkFilename: 'js/[name].[contenthash:8].chunk.js',
assetModuleFilename: 'assets/[contenthash:8][ext]',
publicPath: '/',
clean: true,
},
module: {
rules: [
{ test: /\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
minify: {
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
minifyJS: true,
},
}),
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].chunk.css',
}),
new CompressionWebpackPlugin({
algorithm: 'gzip',
threshold: 10240,
test: /\.(js|css|html|svg)$/,
}),
],
optimization: {
minimize: true,
minimizer: ['...', new CssMinimizerPlugin()],
splitChunks: { chunks: 'all' },
runtimeChunk: 'single',
},
});
小结
生产模式的核心,是用一组默认开启的优化,把代码打磨成适合线上分发的形态:
| 优化 | 目的 |
|---|---|
| Terser 压缩 | 减小 JS 体积 |
| Tree Shaking / Scope Hoisting | 剔除与合并代码 |
process.env.NODE_ENV | 移除开发期分支 |
| contenthash | 长期缓存 |
| CSS 抽离与压缩 | 样式独立、减小体积 |
| gzip 预压缩 | 减小传输体积 |
做法上,记住一句话:开发要快、生产要小。用 mode 区分,用 webpack-merge 拆分,再按需补充压缩、拆分、缓存相关配置,就能得到一份可靠的线上产物。