本文从代码压缩、代码拆分、样式分离等三个方面对生产环境的代码打包进行了优化配置。只是最简单的一些配置,如果真正运用到项目中,还需要根据项目添加更多配置。
前言
本文讲述的是如何对生产环境下的代码进行压缩,如果还不是太了解 Webpack 的朋友,可以先看一下我的上一篇文章:从零开始搭建一个 Webpack 开发环境配置(附 Demo)
本文项目代码位置:源码地址
环境搭建
项目结构
首先编写一个项目,初始化 npm,然后 在本地安装 webpack,接着安装 webpack-cli(此工具用于在命令行中运行 webpack):1
2
3
4
5
6
7$ mkdir webpack-prod-demo
$ cd webpack-prod-demo
$ npm init -y
$ npm install webpack webpack-cli --save-dev
project1
2
3
4
5
6
7webpack-prod-demo
|- package.json
|- /public
|- index.html
|- /src
|- index.js
|- index.css
pubic/index.html1
2
3
4
5
6
7
8
9
10
11
12
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack 生产环境配置</title>
</head>
<body>
</body>
</html>
index.js1
2
3
4
5
6
7
8
9
10
11import './index.css';
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello World';
return element;
}
document.body.appendChild(component());
index.css1
2
3
4div {
color: blue;
text-align: center;
}
package.json1
2
3
4
5
6
7
8
9
10
11
12
13{
"name": "webpack-prod-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
配置 webpack.config.js 文件
在根目录下新建 webpack.config.js 文件,并进行基本配置
安装插件1
2
3
4
5
6
7
8
9
10
11
12
13
14# 安装 babel-loader
$ npm install babel-core babel-loader@7 --save-dev
# 安装 babel presets
$ npm install babel-preset-env babel-preset-stage-0 --save-dev
# 安装 babel plugins
$ npm install babel-plugin-transform-class-properties babel-plugin-transform-runtime babel-runtime --save-dev
# 安装其余所需 loader
$ npm install css-loader style-loader file-loader csv-loader xml-loader html-loader markdown-loader --save-dev
# 安装 webpack 插件
$ npm install clean-webpack-plugin html-webpack-plugin friendly-errors-webpack-plugin --save-dev
配置 webpack.config.js:
webpack.config.js1
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'hidden-source-map',
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HTMLWebpackPlugin({
// 用于生成的HTML文档的标题
title: 'Webpack 生产环境配置',
// webpack 生成模板的路径
template: './public/index.html'
}),
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin(['dist']),
// 在命令行进行友好提示
new FriendlyErrorsWebpackPlugin()
],
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader?cacheDirectory'
},
// 解析 css
{
test: /\.css$/,
include: path.resolve(__dirname, 'src'),
use: [
'style-loader',
// 还可以给 loader 添加一些配置
{
loader: 'css-loader',
options: {
// 开启 sourceMop
sourceMap: true
}
}
]
},
// 解析图片资源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字体
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析数据资源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析数据资源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 文件
{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}
]
}
}
在项目根目录下创建 .babelrc 文件
.babelrc1
2
3
4
5
6
7{
"presets": ["env", "stage-0"],
"plugins": [
"transform-runtime",
"transform-class-properties"
]
}
基本配置完成后运行项目
在命令行运行指令:1
$ npm run build
此时在浏览器打开 dist 文件夹下的 html 文件,页面上正常显示 蓝色居中的 Hello World
查看此时 dist 文件大小:
设置 mode 为 production
webpack.config.js1
2
3
4
5...
module.exports = {
mode: 'production'
...
}
设置为生产环境后运行项目
在命令行运行指令:1
$ npm run build
此时在浏览器打开 dist 文件夹下的 html 文件,页面上还是能够正常显示 蓝色居中的 Hello World
查看此时 dist 文件大小:
本文的重点:优化打包
安装需要用到的插件:1
2
3
4
5# 安装压缩 js、 css 代码的插件
$ npm install uglifyjs-webpack-plugin optimize-css-assets-webpack-plugin --save-dev
# 安装提取 css 的插件
$ npm install mini-css-extract-plugin --save-dev
uglifyjs-webpack-plugin 和 optimize-css-assets-webpack-plugin 的使用
webpack.config.js1
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...;
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
...,
optimization: {
// 打包压缩js/css文件
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
// 在UglifyJs删除没有用到的代码时不输出警告
warnings: false,
// 删除所有的 `console` 语句,可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
},
output: {
// 最紧凑的输出
beautify: false,
// 删除所有的注释
comments: false,
}
}
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
safe: true
}
})
]
}
}
uglifyjs-webpack-plugin 更多配置请参考:插件文档。
optimize-css-assets-webpack-plugin 更多配置请参考:插件文档。
mini-css-extract-plugin 的使用
注意: MiniCssExtractPlugin.loader 和 style-loader 一起使用可能出现问题。所以下面我将 style-loader 去掉了。
webpack.config.js1
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...;
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...
plugins: [
...,
// 打包后提取出css文件
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[name].[contenthash:8].chunk.css'
})
],
module: [
rules: [
...,
// 解析 css
{
test: /\.css$/,
include: path.resolve(__dirname, 'src'),
use: [
{
loader: MiniCssExtractPlugin.loader
},
// 还可以给 loader 添加一些配置
{
loader: 'css-loader',
options: {
// 开启 sourceMop
sourceMap: true
}
}
]
},
...
]
]
}
mini-css-extract-plugin 更多配置请参考:插件文档。
chunk 拆分
webpack.config.js1
2
3
4
5
6
7
8
9
10
11
12
13
14...;
module.exports = {
...,
optimization: {
// 打包压缩js/css文件
minimizer: [
...
],
splitChunks: {
chunks: 'all'
}
}
}
splitChunks 更多配置请参考:官方文档。
区分环境
在开发网页的时候,一般都会有多套运行环境,例如:
- 在开发过程中方便开发调试的环境
- 发布到线上给用户使用的运行环境
为了尽可能的复用代码,在构建的过程中需要根据目标代码要运行的环境而输出不同的代码,我们需要一套机制在源码中去区分环境。可以通过 Webpack 内置的 DefinePlugin 插件进行环境的区分。
区分环境的原因:
很多第三方库中也做了环境区分的优化
- 开发环境:包含类型检查、HTML 元素检查等等针对开发者的警告日志代码
- 线上环境:去掉了所有针对开发者的代码,只保留让 React 能正常运行的部分,以优化大小和性能
注意: NODE_ENV 和 ‘production’ 两个值是社区的约定,通常使用这条判断语句在区分开发环境和线上环境。
配置:
webpack.config.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16...
module.exports = {
...,
plugins: [
...,
// 区分环境
new webpack.DefinePlugin({
// 定义 NODE_ENV 环境变量为 production
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
],
...
}
进行配置优化后运行项目
在命令行运行指令:1
$ npm run build
此时在浏览器打开 dist 文件夹下的 html 文件,页面上仍能正常显示 蓝色居中的 Hello World
查看此时 dist 文件大小:
webpack.config.js 文件最终代码
其余文件基本未进行修改。在此将 webpack.config.js 代码贴出来
webpack.config.js1
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'production',
devtool: 'hidden-source-map',
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HTMLWebpackPlugin({
// 用于生成的HTML文档的标题
title: 'Webpack 开发环境配置',
// webpack 生成模板的路径
template: './public/index.html'
}),
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin(['dist']),
// 在命令行进行友好提示
new FriendlyErrorsWebpackPlugin(),
// 打包后提取出css文件
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[name].[contenthash:8].chunk.css'
}),
// 区分环境
new webpack.DefinePlugin({
// 定义 NODE_ENV 环境变量为 production
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
],
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader?cacheDirectory'
},
// 解析 css
{
test: /\.css$/,
include: path.resolve(__dirname, 'src'),
use: [
{
loader: MiniCssExtractPlugin.loader
},
// 还可以给 loader 添加一些配置
{
loader: 'css-loader',
options: {
// 开启 sourceMop
sourceMap: true
}
}
]
},
// 解析图片资源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字体
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析数据资源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析数据资源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 文件
{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}
]
},
optimization: {
// 打包压缩js/css文件
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
// 在UglifyJs删除没有用到的代码时不输出警告
warnings: false,
// 删除所有的 `console` 语句,可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
},
output: {
// 最紧凑的输出
beautify: false,
// 删除所有的注释
comments: false,
}
}
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
safe: true
}
})
],
splitChunks: {
chunks: 'all'
}
}
}
对比
通过三次打包的对比,可以看到:
- 第一次普通配置打包后,包大小为 48.1 K
- 第二次设置为生产环境后打包,包大小为 39.0 K
- 第三次进行优化配置后打包,包大小为 1.46 K
目前这个项目一个 js 文件,代码量很少,但是还是可以看到优化的效果的。如果项目更大的话,优化的效果也会更明显。当然,需要的配置可能更多了。
项目源码
总结
本文只是对于生产环境下代码打包的简单优化,在项目实战的过程中,可能会需要更多的配置。其实无论看多少教程,其实里面的配置都不一定能够满足自身的要求。在开发的过程中,还是需要自己根据需求进行配置。
其实 Webpack 的学习并不难,根据官网的说明和指南,其实基本都会使用。难的是各种 loader、plugin 自身的配置。这些都需要去到 loader 和 plugin 的网站上深入研究才可以。所以想要更好的进行 webpack 配置,还是需要多多留意最新技术的出现,多搜集有用的插件和配置。积少成多,慢慢的就能配置出更好的项目脚手架(目前这也是我的目标)。