Fork me on GitHub

webpack4笔记(5)-环境分离

开发和生产环境构建配置差异

日常的前端开发工作中,一般都会有两套构建环境:一套开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印 debug 信息,包含 sourcemap 文件;另外一套构建后的结果是直接应用于线上的,即代码都是压缩后,运行时不打印 debug 信息,静态文件不包括 sourcemap 的。有的时候可能还需要多一套测试环境,在运行时直接进行请求 mock 等工作。

Webpack4默认支持mode参数用于区分production环境和development环境。指定使用 production mode 时,默认会启用各种性能优化的功能,包括构建结果优化以及 webpack 运行性能优化,而如果是 development mode 的话,则会开启 debug 工具,运行时打印详细的错误信息,以及更加快速的增量编译构建。

webpack 的 mode 参数已经给我们带来了一些很方便的环境差异化配置,但是针对一些项目情况,例如使用 css-loader 或者 url-loader 等,不同环境传入 loader 的配置也不一样,而 mode 并没有帮助我们做这些事情,因此有些配置还是需要手动区分环境后来进行调整。

在配置文件中区分 mode

4.x 使用 mode 参数,3.x 使用 Node.js 的 process.env.NODE_ENV

webpack 的运行时环境是 Node.js,我们可以通过 Node.js 提供的机制给要运行的 webpack 程序传递环境变量,来控制不同环境下的构建行为。例如,我们在 npm 中的 scripts 字段添加一个用于生产环境的构建命令:

3.x版本:

1
2
3
4
5
6
{
"scripts": {
"build": "NODE_ENV=production webpack",
"develop": "NODE_ENV=development webpack-dev-server"
}
}

4.x版本

1
2
3
4
"scripts": {
"build": "webpack --mode production",
"start": "webpack-dev-server --open --mode development"
},

常见的环境差异配置

  • 生产环境可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件
  • 生产环境需要压缩 HTML/CSS/JS 代码
  • 生产环境需要压缩图片
  • 开发环境需要生成 sourcemap 文件
  • 开发环境需要打印 debug 信息
  • 开发环境需要 live reload 或者 hot reload 的功能

webpack 4.x 的 mode 已经提供了上述差异配置的大部分功能,mode 为 production 时默认使用 JS 代码压缩,而 mode 为 development 时默认启用 hot reload,等等。这样让我们的配置更为简洁,我们只需要针对特别使用的 loader 和 plugin 做区分配置就可以了。

模式 默认配置
production 通过DefinePlugin设置 process.env.NODE_ENV=production。启用FlagDependencyUsagePlugin、FlagIncludedChunksPlugin、ModuleConcatenationPlugin、NoEmitOnErrorsPlugin、OccurrenceOrderPlugin、SideEffectsFlagPlugin 和 UglifyJsPlugin插件。
development 通过DefinePlugin设置process.env.NODE_ENV=development。启用 NamedChunksPlugin和NamedModulesPlugin插件。

webpack 3.x 版本还是只能自己动手修改配置来满足大部分环境差异需求。

拆分配置

由于2个环境的配置有相同部分又有差异部分,因此通常的做法是提供3个webpack配置文件,一个是公有的配置文件,一个是针对production的配置文件,一个针对development,后两个配置文件通过webpack-merge引入公有的配置文件。

  • webpack.base.js:基础部分,即多个文件中共享的配置
  • webpack.development.js:开发环境使用的配置
  • webpack.production.js:生产环境使用的配置
  • webpack.test.js:测试环境使用的配置

我们通过配置mode参数声明当前配置所处的环境。另外,还需要修改一下package.json中的scripts:

1
2
3
4
5
6
...
"scripts": {
"build": "webpack --config webpack.prod.conf.js",
"start": "webpack-dev-server --open --config webpack.dev.conf.js"
},
...

source map

source map有多种风格配置,不同风格各有利弊,对于不同的环境推荐配置如下:

  • development:inline-source-map
  • production:source-map

代码分割

production环境通常需要进行代码分割,而development环境则没有这样的需求。

  1. 分割javascript代码

对webpack的运行时代码进行抽离:

1
2
3
4
5
6
...
optimization: {
runtimeChunk: {
"name": "manifest"
},
...

另外,所有第三方库单独抽离:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
splitChunks: {
cacheGroups: {
default: false,
vendors: false,
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
...

为了支持缓存,加入HashedModuleIdsPlugin插件:

1
2
3
4
5
...
plugins: [
new webpack.HashedModuleIdsPlugin()
]
...
  1. 分割css代码

当前,css代码被style-loader直接添加到js文件里面了,在production环境中,我们通常希望将css文件单独抽离出来,此时可以采用mini-css-extract-plugin插件。

对于production环境,我们将使用MiniCssExtractPlugin.loader替换style-loader:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
localIdentName: '[name]---[local]---[hash:base64:5]'
}
}
]
}
]
}
...

...
new MiniCssExtractPlugin({
filename: "[name].[hash].css",
chunkFilename: "[name].[hash].css"
})
...

还需要将css文件分割到单独的css chunk中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
splitChunks: {
cacheGroups: {
default: false,
vendors: false,
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
},
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
...

webpack.base.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');

module.exports = {
entry: {
'index': './src/index.js',
},
output: {
filename: '[name].[hash].js',
chunkFilename: '[name].[hash].js',
path: path.resolve(__dirname, 'dist')
},

module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [path.resolve(__dirname, 'src')],
exclude: [path.resolve(__dirname, 'node_modules')]
}
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
})
]
};

webpack.development.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const merge = require('webpack-merge');
const webpack = require('webpack');
const baseWebpackConfig = require('./webpack.base);

const webpackConfig = merge(baseWebpackConfig, {
//environment specific config goes here
mode: 'development',
output: {
filename: '[name].js',
chunkFilename: '[name].js'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
inline: true,//do not use iframe for the page, true is default
open: true,//open browser after dev server starts
port: 8080,//8080 is default
proxy: {//proxy backend api
'/api': 'http://localhost:3000'
},
hot: true
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
localIdentName: '[name]---[local]---[hash:base64:5]'
}
}
]
}
]
},
plugins: [
// 配置HMR
new webpack.HotModuleReplacementPlugin()
]

});

module.exports = webpackConfig;

webpack.production.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
const merge = require('webpack-merge');
const webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const baseWebpackConfig = require('./webpack.base');

const webpackConfig = merge(baseWebpackConfig, {
//environment specific config goes here
mode: 'production',
devtool: 'source-map',
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
localIdentName: '[name]---[local]---[hash:base64:5]'
}
}
]
}
]
},
optimization: {
runtimeChunk: {
"name": "manifest"
},
splitChunks: {
cacheGroups: {
default: false,
vendors: false,
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
},
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new MiniCssExtractPlugin({
filename: "[name].[hash].css",
chunkFilename: "[name].[hash].css"
})
]
});

module.exports = webpackConfig;

本文标题:webpack4笔记(5)-环境分离

文章作者:tongtong

发布时间:2019年04月21日 - 22:04

最后更新:2019年04月25日 - 20:04

原始链接:https://ilove-coding.github.io/2019/04/21/webpack4笔记(5)-环境分离/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!
-------------本文结束-------------