# 第7章 生产环境配置
# 环境配置的封装
// package.json
"scripts": {
"dev": "ENV=development webpack-dev-server",
"build": "ENV=production webpack"
}
// webpack.config.js
const ENV = process.env.ENV;
2
3
4
5
6
7
8
// package.json
"scripts": {
"dev": "webpack-dev-server --config=webpack.development.config.js",
"build": "webpack --config=webpack.production.config.js"
}
2
3
4
5
# 开启production模式
# 环境变量
// webpack.config.js
const webpack = require('webpack');
module.exports = {
entry: './app.js',
+ mode: 'production',
- plugins: [
- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
// app.js
console.log(ENV)// production
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DefinePlugin在替换环境变量时对于字符串类型的值进行的是完全替换。不添加JSON.stringify,在替换后就会成为变量名,而非字符串值。所以字符串或者包含字符串的对象都要加上JSON.stringify。
new webpack.DefinePlugin({
ccc: JSON.stringify({
a: 'abc'
})
})
2
3
4
5
webpack4中如果启用了mode: production,则已经设置好process.env.NODE_ENV=production。
# source map
source map指的是将编译、打包、压缩后的代码映射回源代码的过程。
我们启动了devtool配置项,source map就会跟随源代码一步步被传递,直到生成最后的map文件。bundle.js.map。
map文件只会在浏览器打开开发者工具时被加载。所以对用户使用是没有任何影响的,但是任何人都可以看到源码。
# 配置
对于js
module.exports = {
devtool: 'source-map'
}
2
3
对于CSS、SCSS、LESS来说,还需要:
devtool: 'source-map',
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
},
]
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
之后就可以在开发者工具“Sources”下的“webpack://”目录中找到工程源码。
生成完整source map会延长整体构建时间,如果对打包速度需求比较高的话,可以选择一个简化版的source map。cheap-module-eval-source-map属于打包速度和源码信息还原程度的一个良好的折中。
hidden-source-map
webpack仍然会产出完整的map文件,只不过不会在bundle文件中添加对于map文件的引用。
nosources-source-map
打包部署后可以在Sources选项卡中看到源码的目录结构,但是文件的具体内容会被隐藏起来。
生成环境中还可以使用source map,然后通过设置nginx将.map文件只对固定的白名单(公司内网开发),这样用户看不到源码,我们能看到源码。
# 资源压缩
# 压缩JavaScript
webpack4中默认使用了terser的插件terser-webpack-plugin。
webpack3中需要调用webpack.optimize.UglifyJsPlugin。
terser-webpack-plugin支持ES6+代码的压缩。
// webpack version < 4
plugins: [new webpack.optimize.UglifyJsPlugin()]
2
webpack4之后,这项配置被移到了config.optimization.minimize。如果mode:production则不需要人为设置。
module.exports = {
optimization: {
minimize: true
}
}
2
3
4
5
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
test: /\.js$/,
cache: true,
parallel: true, // 多个进程进行压缩
})
]
}
}
2
3
4
5
6
7
8
9
10
11
12
version4: https://v4.webpack.docschina.org/plugins/terser-webpack-plugin
version5: https://webpack.js.org/plugins/terser-webpack-plugin/
# 压缩CSS
压缩CSS文件前提是使用extract-text-webpack-plugin或mini-css-extract-plugin将样式提取出来,接着使用optimize-css-assets-webpack-plugin来进行压缩。
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
}
]
},
plugins: [new ExtractTextPlugin('style.css')],
optimization: {
minimizer: [
new OptimizeCSSAssetsPlugin({
// 压缩处理器的配置
cssProcessorOptions: {
discardComments: {
removeAll: true
}
},
// 是否展示log
canPrint: true
})
]
}
}
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
# 缓存
# 资源hash
当代码发生变化时相对应的hash也会发生变化。
output: {
filename: 'bundle@[chunkhash].js'
}
2
3
# 输出动态HTML
解决了每次更改后修改资源引用路径的困难。使用了html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin();
]
}
2
3
4
5
6
# 使chunk id更稳定
output: {
filename: '[name]@[chunkhash].js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
})
]
2
3
4
5
6
7
8
新模块插入进来时就会导致其他模块的chunk hash发生变化,静态文件文件名会变化,会导致客户端重新下载整个文件。
解决方法webpack3中内部自带了HashedModuleIdsPlugin,它可以为每个模块按照其所在路径生成一个字符串类型的hash id。
plugins: [
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
})
]
2
3
4
5
6
webpack3以下的版本可以使用webpack-hashed-module-id-plugin插件。
webpack4及以上已经修改了模块id的生成机制,不再会有该问题。
# bundle体积监控和分析
const Analyzer = require('webpack.bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new Analyzer();
]
}
2
3
4
5
6
项目启动后就会生成一张bundle的模块组成结构图。