webpack是一种前端资源构建工具,一个静态模块打包器(module bundler)。前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源
1. 五个核心概念
1.1. Entry
入口指示webpack以哪个文件为入口起点开始打包,分析构建内部依赖图
1.2. Output
输出指示webpack打包后的资源bundles输出到哪里去,以及如何命名
1.3. Loader
让webpack能够去处理哪些非JavaScript文件,webpack自身只理解JavaScript
1.4. Plugins
插件可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等
1.5. Mode
模式(Mode)指示 webpack 使用相应模式的配置
选项 | 描述 | 特点 |
---|---|---|
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置 为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin | 能让代码本地调试运行的环境 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置 为 production 启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin |
能让代码优化上线 运行的环境 |
2. webpack 的初体验
2.1. 初始化配置
https://www.webpackjs.com/guides/installation/
新建文件夹 【webpack-demo】
mkdir webpack-demo && cd webpack-demo
初始化 package.json ,输入指令:
npm init -y
下载并安装 webpack,输入指令:
npm install webpack webpack-cli --save-dev
webpack-cli(此工具用于在命令行中运行 webpack)
在安装一个要打包到生产环境的安装包时,使用
npm install --save
,如果是安装一个用于开发环境的安装包时,使用
npm install --save-dev
请在 npm 文档 中查找更多信息。
建立目录结构
1
2
3
4
5webpack-demo
|- package.json
+ |- index.html
+ |- /src
+ |- index.jsindex.js
1
2
3
4const div = document.createElement("div");
div.innerHTML = 'Hello Webpack';
document.body.appendChild(div);index.html
1
2
3
4
5
6
7
8
9
10
11
12
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack-demo</title>
</head>
<body>
<script src="./src/index.js"></script>
</body>
</html>package.json
确保安装包是
私有的(private)
,并且移除main
入口。这可以防止意外发布代码1
2+ "private": true,
- "main": "index.js",执行打包命令
npx webpack,
会将脚本作为入口起点,然后 输出 为main.js
1
2
3npx webpack
asset main.js 105 bytes [emitted] [minimized] (name: main)
./src/index.js 108 bytes [built] [code generated]修改index.html的script脚本
1
<script src="./dist/main.js"></script>
在浏览器中打开
index.html
,能看到以下文本:Hello Webpack
2.2. 编译打包应用
运行指令
开发环境指令:
npx webpack --mode=development
功能:webpack 能够编译打包 js 和 json 文件,并且能将 es6 的模块化语法转换成浏览器能识别的语法
生产环境指令:npx webpack --mode=production
功能:在开发配置功能上多一个功能,压缩代码
结论
webpack 能够编译打包 js 和 json 文件
能将 es6 的模块化语法转换成浏览器能识别的语法
能压缩代码
问题
不能编译打包 css、img 等文件
不能将 js 的 es6 基本语法转化为 es5 以下语法
3. webpack开发环境的基本配置
在 webpack 4 中,可以无须任何配置使用,然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件
3.1. 创建配置文件
创建文件
webpack.config.js
这是webpack的配置文件,指示webpack干哪些活
所有构建工具都是基于nodejs平台运行的,模块化默认采用commonjs
配置内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// node 内置核心模块,用来处理路径问题
const { resolve } = require('path');
module.exports = {
//入口文件
entry: './src/js/index.js',
//输出配置
output: {
// 输出文件名
filename: 'index.js',
// 输出文件路径配置
path: resolve(_dirname,'build/js')
},
//开发环境
mode: 'development'
};运行指令:
1
npx webpack --config webpack.config.js
如果
webpack.config.js
存在,则webpack
命令将默认选择使用它。这里使用
--config
选项只是表明可以传递任何名称的配置文件这对于需要拆分成多个文件的复杂配置是非常有用
目录结构
1
2
3
4
5
6
7
8
9
10webpack-demo
|- package.json
|- webpack.config.js
|- /build
|- index.html
|- js
|- index.js
|- /src
|- js
|- index.js
在 package.json 中添加 "build": "webpack"
1 | "scripts": { |
现在,可以使用 npm run build
命令,来替代之前使用的 npx
命令
注意,使用 npm 的 scripts
,我们可以像使用 npx
那样通过模块名引用本地安装的 npm 包
3.2. 打包样式资源
3.2.1. css文件
创建文件 index.css,放入 /src/css 文件夹下
1
2
3
4
5div{
width: 200px;
height: 200px;
background-color: pink;
}在index.js中引入样式资源
1
import '../css/style.css';
下载安装loader包
1
npm install --save-dev style-loader css-loader
修改配置文件
webpack 根据正则表达式,来确定应该查找哪些文件,并将其提供给指定的 loader
在这种情况下,以
.css
结尾的全部文件,都将被提供给style-loader
和css-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
28
29
30
31
32
33
34
35
36
37
38// resolve 用来拼接绝对路径的方法
const { resolve } = require("path");
module.exports = {
// webpack 配置
// 入口起点
entry: './src/js/index.js',
// 输出
output: {
// 输出文件名
filename: 'index.js',
// 输出路径
// __dirname nodejs 的变量,代表当前文件的目录绝对路径
path: resolve(__dirname,'build/js')
},
// loader 的配置
module: {
// 详细 loader 配置
// 不同文件必须配置不同 loader 处理
rules: [
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪些 loader 进行处理
// use 数组中 loader 执行顺序:从右到左,从下到上 依次执行
use: [
// 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
'style-loader',
// 将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串
'css-loader'
]
}
]
},
// 模式
// 开发模式
mode: 'development'
}运行指令:
npm run build
再次在浏览器中打开
index.html
,你应该看到Hello Webpack
现在所在的div背景颜色是粉红色并查看页面的 head 标签,在
index.js
中导入了 style 块元素
3.2.2. less文件
新建 index.less,放入 /src/css 文件夹下
1
2
3body{
background-color: blue;
}下载安装包
1
npm i less-loader less --save-dev
修改配置文件
添加解析less的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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48// resolve 用来拼接绝对路径的方法
const { resolve } = require("path");
module.exports = {
// webpack 配置
// 入口起点
entry: './src/js/index.js',
// 输出
output: {
// 输出文件名
filename: 'index.js',
// 输出路径
// __dirname nodejs 的变量,代表当前文件的目录绝对路径
path: resolve(__dirname,'build/js')
},
// loader 的配置
module: {
// 详细 loader 配置
// 不同文件必须配置不同 loader 处理
rules: [
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪些 loader 进行处理
// use 数组中 loader 执行顺序:从右到左,从下到上 依次执行
use: [
// 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
'style-loader',
// 将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串
'css-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// 将 less 文件编译成 css 文件
// 需要下载 less-loader 和 less
'less-loader'
]
}
]
},
// 模式
// 开发模式
mode: 'development'
}运行命令
1
npm run build
3.3. 打包 HTML 资源
https://github.com/jaketrent/html-webpack-template
创建index.html,放于 src 文件夹下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack-demo</title>
</head>
<body>
<div class="hello">
<h1>Hello Webpack</h1>
</div>
</body>
</html>下载安装 plugin 包
1
npm i --save-dev html-webpack-plugin
修改配置文件
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
26const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// loader 的配置
]
},
plugins: [
// plugins 的配置
// html-webpack-plugin
// 功能:默认会创建一个空的 HTML,自动引入打包输出的所有资源(JS/CSS)
// 需求:需要有结构的 HTML 文件
new HtmlWebpackPlugin({
// 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
template: './src/index.html'
})
],
mode: 'development'
};运行指令
1
npm run build
3.4. 打包图片资源
创建文件夹 imgs,放于 src 中,并放入两张照片
1
2
3
4
5
6
7
8
9
10
11
12
13webpack-demo
|- package.json
|- webpack.config.js
|- /src
|- css
|- style.css
|- index.less
|- imgs
|- 1.png
|- 2.png
|- js
|- index.js
|- index.htmlindex.html 中添加内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="hello">
<h1>Hello Webpack</h1>
<img src="./imgs/1.png" alt="">
</div>
<div id="box1"></div>
<div id="box2"></div>
</body>
</html>index.less 中添加内容
1
2
3
4
5
6
7
8
9
10
11#box1{
width: 100px;
height: 100px;
background: url('../imgs/1.png') no-repeat center;
}
#box2{
width: 100px;
height: 100px;
background: url('../imgs/2.png') no-repeat center;
}index.js中引入 index.less
1
import './index.less';
index.css 中添加内容
1
2
3
4
5
6
7
8
9.hello{
width: 400px;
height: 400px;
margin: 0 auto;
text-align: center;
}
.hello img{
width: 100%;
}index.js 中引入 index.css
1
2import '../css/style.css';
import '../css/index.less';下载安装 loader 包
1
npm install --save-dev html-loader url-loader file-loader
修改配置文件
需要在options里添加一个属性
esModule:false
和在rules对象中添加type:'javascript/auto'
,解决在打包css文件的背景图路径错误以及多生成了图片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
55const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.less$/,
// 要使用多个 loader 处理用 use
use: ['style-loader', 'css-loader', 'less-loader']
},
{
// 问题:默认处理不了 html 中 img 图片
// 图片
test: /\.(png|gif|svg|jpg)$/,
loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base64处理
// 优点: 减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 问题:因为url-loader默认使用es6模块化解析,
// 而html-loader引入图片是commonjs
// 解析时会出问题:[object Module]
// 解决:关闭 url-loader 的 es6 模块化,使用 commonjs 解析
esModule: false,
// 给图片进行重命名
// [hash:10]取图片的 hash 的前 10 位
// [ext]取文件原来扩展名
name: 'imgs/[name].[hash:10].[ext]'
// ,outputPath: 'imgs'
},
//这个属性如果没有设置,则会生成两张图片(如果你的页面只引入了一张图片)
type: 'javascript/auto'
},
{
test: /\.html$/,
// 处理 html 文件的 img 图片
// (负责引入 img,从而能被 url-loader 进行处理)
loader: 'html-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
};运行指令:
1
npm run build
得到的build文件夹目录结构
1 | webpack-demo |
build/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack-demo</title>
<script defer src="js/index.js"></script><link href="css/main.css" rel="stylesheet"></head>
<body>
<div class="hello">
<h1>Hello Webpack</h1>
<img src="imgs/1.db787884f1.png" alt="">
</div>
<div id="box1"></div>
<div id="box2"></div>
</body>
</html>
3.5. 打包字体资源
下载字体图标库 fontawesome,放入 src/libs 中
在 src/js/index.js中添加 fontawesome.min.css
1
import '../libs/font-awesome-4.7.0/css/font-awesome.min.css';
在 src/index.html中添加一个图标
1
2
3
4
5<div class="hello">
<i class="fa fa-bath"></i>
<h1>Hello Webpack</h1>
<img src="./imgs/1.png" alt="">
</div>修改配置文件
loader中添加针对字体的配置
1
2
3
4
5
6
7
8
9
10{
// 处理字体
test: /\.(eot|svg|ttf|woff|woff2)\w*/,
loader: 'file-loader',
options: {
name: 'font/[name].[ext]',
esModule: false
},
type: 'javascript/auto'
}整体配置
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
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// resolve 用来拼接绝对路径的方法
const { resolve } = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// webpack 配置
// 入口起点
entry: './src/js/index.js',
// 输出
output: {
// 输出文件名
filename: 'js/index.js',
// 输出路径
// __dirname nodejs 的变量,代表当前文件的目录绝对路径
path: resolve(__dirname,'build')
},
// loader 的配置
module: {
// 详细 loader 配置
// 不同文件必须配置不同 loader 处理
rules: [
{
// 匹配css文件
test: /\.css$/,
// 使用哪些 loader 进行处理
// use 数组中 loader 执行顺序:从右到左,从下到上 依次执行
use: [
// 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
'style-loader',
// 将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串
'css-loader'
]
},
{
// 匹配less文件
test: /\.less$/,
use: [
'style-loader',
'css-loader',
// 将 less 文件编译成 css 文件
// 需要下载 less-loader 和 less
'less-loader'
]
},
{
// 问题:默认处理不了 html 中 img 图片
// 图片
test: /\.(png|gif|svg|jpg|jpeg)$/,
loader: 'url-loader',
options: {
// 图片大小小于8kb,就会被base64处理
// 优点: 减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8 * 1024,
// 问题:因为url-loader默认使用es6模块化解析,
// 而html-loader引入图片是commonjs
// 解析时会出问题:[object Module]
// 解决:关闭 url-loader 的 es6 模块化,使用 commonjs 解析
esModule: false,
// 给图片进行重命名
// [hash:10]取图片的 hash 的前 10 位
// [ext]取文件原来扩展名
name: 'imgs/[name].[hash:10].[ext]'
// ,outputPath: 'imgs'
},
//这个属性如果没有设置,则会生成两张图片(如果你的页面只引入了一张图片)
type: 'javascript/auto'
},
{
// 处理 html 文件的 图片
test: /\.html$/,
// (负责引入 img,从而能被 url-loader 进行处理)
loader: 'html-loader'
},
{
// 处理字体
test: /\.(eot|svg|ttf|woff|woff2)\w*/,
loader: 'file-loader',
options: {
name: 'font/[name].[ext]',
esModule: false
},
type: 'javascript/auto'
}
]
},
plugins: [
new htmlWebpackPlugin({
template: './src/index.html'
})
],
// 模式
// 开发模式
mode: 'development'
}
3.6. 打包其他资源
1 | { |
3.7. 清除文件
由于过去的代码示例遗留下来,webpack 会生成文件,然后将这些文件放置在 /build
文件夹中,但是 webpack 无法追踪到哪些文件是实际在项目中用到的。通常,在每次构建前清理文件夹,是比较推荐的做法,因此只会生成用到的文件。
安装插件
1
npm install clean-webpack-plugin --save-dev
修改配置
1
2
3
4
5const { CleanWebpackPlugin } = require("clean-webpack-plugin");
plugins: [
new CleanWebpackPlugin()
]
3.8. devserver
https://www.webpackjs.com/configuration/dev-server/
开发服务器devserver,用来自动化,自动编译,自动打开浏览器,自动刷新浏览器
特点:只会在内存中编译打包,不会有任何输出
下载安装包
1
npm i webpack-dev-server --save-dev
修改配置文件
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
46const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
// 打包其他资源(除了html/js/css资源以外的资源)
{
// 排除 css/js/html 资源
exclude: /\.(css|js|html|less)$/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer:{
// 查找文件
static: resolve(__dirname, 'build'),
// 启动 gzip 压缩
compress: true,
// 端口号
port: 3000,
// 自动打开浏览器
open: true,
// 开启HMR功能
// 当修改了 webpack 配置,新配置要想生效,必须重新 webpack 服务
hot: true
}
};运行指令:
npx webpack-dev-server
添加一个 script 脚本,可以直接运行开发服务器(dev server),在package.json 中
1
2
3
4
5"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server",
"build": "webpack"
}现在,可以在命令行中运行
npm start
,就会看到浏览器自动加载页面
3.9. 开发环境配置
创建文件
src
— css
— imgs
— js
— media
— index.html
webpack.config.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
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// 绝对路径拼接
const {resolve} = require("path");
// css单独文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 打包html资源
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 清除文件
const {CleanWebpackPlugin} = require("clean-webpack-plugin");
module.exports = {
// 入口文件
entry: './src/js/index.js',
// 输出配置
output: {
// 输出文件名
filename: 'js/index.js',
// 输出路径
// __dirname nodejs 的变量,代表当前文件的目录绝对路径
path: resolve(__dirname,'build')
},
// loader 的配置
module: {
// 详细 loader 配置
// 不同文件必须配置不同 loader 处理
rules: [
{
// css文件
test: /\.css$/,
// 使用哪些 loader 进行处理
// use 数组中 loader 执行顺序:从右到左,从下到上 依次执行
use: [
// 创建 style 标签,将 js 中的样式资源插入进行,添加到 head 中生效
// 'style-loader',
// 这个 loader 取代 style-loader。
// 作用:提取 js 中的 css 成单独文件
MiniCssExtractPlugin.loader,
// 将 css 文件变成 commonjs 模块加载 js 中,里面内容是样式字符串
'css-loader'
]
},
{
// less文件
test: /\.less/,
use: [
// 'style-loader',
// 这个 loader 取代 style-loader。
// 作用:提取 js 中的 css 成单独文件
MiniCssExtractPlugin.loader,
'css-loader',
// 将 less 文件编译成 css 文件
// 需要下载 less-loader 和 less
'less-loader'
]
},
{
// 图片
// 问题:默认处理不了 html 中 img 图片
test: /\.(png|gif|jpg|jpeg)$/,
loader: 'url-loader',
options: {
// 给图片进行重命名
// [hash:10]取图片的 hash 的前 6 位
// [ext]取文件原来扩展名
name: 'imgs/[name].[hash:6].[ext]',
// 问题:因为url-loader默认使用es6模块化解析,
// 而html-loader引入图片是commonjs
// 解析时会出问题:[object Module]
// 解决:关闭 url-loader 的 es6 模块化,使用 commonjs 解析
esModule: false,
// 图片大小小于8kb,就会被base64处理
// 优点: 减少请求数量(减轻服务器压力)
// 缺点:图片体积会更大(文件请求速度更慢)
limit: 8*1024
},
//这个属性如果没有设置,则会生成两张图片(如果你的页面只引入了一张图片)
type: 'javascript/auto'
},
{
// 处理 html 文件的 图片
test: /\.html$/,
// (负责引入 img,从而能被 url-loader 进行处理)
loader: 'html-loader'
},
{
// 字体
test: /\.(eot|svg|ttf|woff|woff2|otf)/,
loader: 'file-loader',
options: {
name: 'font/[name].[ext]',
esModule: false
},
type: 'javascript/auto'
},
{
// 其他资源
exclude: /\.(css|html|js|less|png|gif|jpg|jpeg|eot|svg|ttf|woff|woff2|otf)$/,
loader: 'file-loader',
options: {
name: 'libs/[name].[ext]',
esModule: false
},
type: 'javascript/auto'
}
]
},
plugins:[
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/main.css'
}),
new CleanWebpackPlugin()
],
// 模式
// 开发模式
mode: 'development',
devServer: {
// 项目构建后路径
static: resolve(__dirname, 'build'),
// 启动 gzip 压缩
compress: true,
// 端口号
port: 3000,
// 自动打开浏览器
open: true,
// 开启HMR功能
// 当修改了 webpack 配置,新配置要想生效,必须重新 webpack 服务
hot: true
}
}运行指令: npm start
4. webpack 生产环境的基本配置
4.1. 提取 css 成单独文件
下载插件
npm install --save-dev mini-css-extract-plugin
修改配置文件
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
36const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build') },
module: {
rules: [
{
test: /\.css$/,
use: [
// 创建 style 标签,将样式放入
// 'style-loader',
// 这个 loader 取代 style-loader。
// 作用:提取 js 中的 css 成单独文件
MiniCssExtractPlugin.loader,
// 将css文件整合到js文件中
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/built.css'
})
],
mode: 'development'
};运行指令:
1
npm run build
4.2. css 兼容性处理
https://webpack.docschina.org/loaders/postcss-loader/
在 src/css/style.css 中添加一些css3样式
1
2
3
4
5
6
7
8.hello{
width: 400px;
height: 400px;
margin: 0 auto;
text-align: center;
display: flex;
backface-visibility: hidden;
}下载 loader
1
npm install --save-dev postcss-loader postcss-preset-env
修改配置文件
node默认是生产环境,需要
process.env.NODE_ENV
修改环境变量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// 设置 nodejs 环境变量
process.env.NODE_ENV = 'development';
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
/*
css兼容性处理:postcss -> postcss-loader postcss-preset-env
帮postcss找到pakcage.json中browerslist里面的配置
通过配置加载指定的兼容性
https://github.com/browserslist/browserslist
*/
// 使用loader的默认配置
// postcss-loader
// 修改loader配置
{
loader: 'postcss-loader',
options: {
postcssOptions:{
plugins:[
'postcss-preset-env'
]
}
}
}
]
}
]
}修改 package.json
https://github.com/browserslist/browserslist
1
2
3
4
5
6
7
8
9
10
11
12"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}运行命令
1
npm run build
查看 build/css/main.css
1
2
3
4
5
6
7
8
9.hello{
width: 400px;
height: 400px;
margin: 0 auto;
text-align: center;
display: flex;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
4.3. 压缩 css
https://webpack.docschina.org/plugins/css-minimizer-webpack-plugin/
下载安装包
1
npm install css-minimizer-webpack-plugin --save-dev
修改配置文件
1
2
3
4
5
6
7
8
9
10
11
12// 压缩css
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new CssMinimizerPlugin()
],
// 开发环境启用css优化
minimize: true
}
}
4.4. js 语法检查
https://webpack.docschina.org/plugins/eslint-webpack-plugin/#root
https://www.npmjs.com/package/eslint-config-airbnb-base
下载安装包
1
npm install --save-dev eslint-webpack-plugin eslint eslint-config-airbnb-base eslint-plugin-import
在 src/js/index.js 中添加函数
1
2
3
4const sum = (a,b)=>{
return a+b;
}
console.log(sum(1,2));修改配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const { resolve } = require('path');
// js语法检查
const EslintPlugin = require("eslint-webpack-plugin");
module.exports = {
plugins:[
// js检查
new EslintPlugin({
// 指定文件根目录,类型为字符串
context: resolve(__dirname,"src"),
// 指定需要检查的扩展名
extensions: "js",
// ESLint 自动修复特性
fix: true
})
]
};配置 package.json
https://github.com/airbnb/javascript
1
2
3
4
5
6
7
8
9"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true,
"es6": true,
"node": true,
"jquery": true
}
}运行指令:
1
npm run build
终端出现 warning Unexpected console statement no-console
修改 index.js,添加注释
在【生产环境】中应该避免使用console
1
2
3
4
5
6const sum = (a,b)=>{
return a+b;
}
// 下一行不进行eslint检查
// eslint-disable-next-line
console.log(sum(1,2));再次打开 src/js/index.js
1
2const sum = (a, b) => a + b;
console.log(sum(1, 2));
4.5. js 兼容性处理
修改 src/js/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13const sum = (a, b) => a + b;
// 下一行不进行eslint检查
// eslint-disable-next-line
console.log(sum(1, 2));
const p = new Promise((resolve) => {
resolve('data');
});
p.then((data) => {
// eslint-disable-next-line
console.log(data);
});下载安装包
1
npm i --save-dev babel-loader @babel/core @babel/preset-env
基本js兼容性处理 –> @babel/preset-env
只能转换基本语法,promise就不能转换
全部js兼容性处理 –> @babel/polyfill
1
npm i @babel/polyfill -S
src/js/index.js 中引入该插件
1
import '@babel/polyfill';
将所有兼容性代码全部引入,体积太大
按需加载 –> corejs
1
2npm i core-js -S
npm i regenerator-runtime -S
修改配置文件
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
40module.exports = {
module: {
rules: [
{
// js文件-兼容不同浏览器
test: /\.js$/,
// 排除模块文件夹
exclude: /node-modules/,
loader: 'babel-loader',
// 预设:指示 babel 做怎么样的兼容性处理
options: {
presets: [
[
'@babel/preset-env',
{
// 会根据 target 属性配置的目标环境,找出需要的ES6API进行导入
// 引入目标环境不支持的所有 API
useBuiltIns: 'entry',
// 指定 core-js 版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '6',
safari: '10',
edge: '17'
}
}
]
]
}
}
]
}
};运行指令:
1
npm run build
打开 build/js/index.js,发现代码已经改变
1
var sum = function sum(a, b) {\n return a + b;\n};
4.6. js 压缩
https://webpack.docschina.org/plugins/terser-webpack-plugin#uglify-js
该插件使用 terser 来压缩 JavaScript
下载安装
1
npm install terser-webpack-plugin --save-dev
修改配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 压缩js
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
// 压缩js
new TerserPlugin({
// 用来匹配需要压缩的文件
test: /\.js$/,
// 匹配不需要压缩的文件
exclude: /node_modules/,
// 是否将注释剥离到单独的文件中
extractComments: false,
// 删除注释
terserOptions: {
format: {
comments: false,
}
}
})
]
};
4.7. HTML 压缩
方式一:转换为生产模式
https://webpack.docschina.org/guides/production/#minification
mode改为生产模式production,生产环境下默认使用
TerserPlugin
方式二:修改参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18module.exports = {
plugins:[
// 打包html
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
],
// 模式
// 开发模式
mode: 'development'
};
4.8. 生产环境配置
1 | // 拼接文件绝对路径 |
5. webpack 优化配置
5.1. HMR
https://webpack.docschina.org/guides/hot-module-replacement/
Hot Module Replacement 热模块替换
在devServer工作时,一个模块发生变化,只会重新打包这个模块
在webpack.config.js中修改devServer的配置
样式文件
:可以使用HMR功能,style-loader内部实现了js文件
:默认不能使用HMR只能处理非入口文件
添加支持 HMR的代码
1
2
3
4
5
6
7//print.js
function print() {
// eslint-disable-next-line
console.log('print.js');
}
export default print;src/js/index.js
1
2
3
4
5
6
7
8
9
10import print from './print';
if (module.hot) {
// 一旦hot为true,说明开启了HMR功能,让代码生效
// 方法会监听print.js文件的变化,一旦发生变化,其他默认不会重新打包构建
// 执行回调函数
module.hot.accept('./print.js', () => {
print();
});
}html文件
:默认不能使用HMR,html文件不能更新了修改entry,引入html文件
但还是不支持HMR
1 | entry: ['./src/js/index.js','./src/index.html'], |
5.2. source-map
一种提供源代码到构建代码映射的技术
如果构建后的代码出错了,通过映射可以追踪源代码
https://webpack.docschina.org/configuration/devtool/#root
使用source-map
1 | devtool: 'source-map' |
模式可以是
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
参数 | 生成位置 | 错误提示 |
---|---|---|
source-map | 外部文件 | 错误代码准确信息 源代码的错误位置 |
inline-source-map | 只生成一个内联在js中 | 错误代码准确信息 源代码的错误位置 |
hidden-source-map | 外部文件 | 错误代码错误原因,没有错误位置 不能追踪源代码错误,只能提示到构建后代码的错误位置 |
eval-source-map | 内联在js中,每个eval对应一个eval-source-map | 错误代码准确信息 源代码的错误位置 |
nosources-source-map | 外部文件 | 错误代码准确信息 但是没有任何源代码信息 |
cheap-source-map | 外部文件 | 错误代码准确信息 源代码的错误位置 只能准确到行,其他的能准确到行列 |
cheap-module-source-map | 外部文件 | 错误代码准确信息 源代码的错误位置 只能准确到行,其他的能准确到行列 会将loader的source map加入 |
内联 vs 外部:外部生成了独立的文件,内联构建速度更快
开发环境:速度+调试友好
eval-source-map / eval-cheap-module-source-map
速度 eval>inline>cheap
eval-cheap-source-map
eval-source-map
调试
source-map
cheap-module-source-map cheap-source-map
生产环境:源代码隐藏+调试友好
source-map / cheap-module-source-map
内联会让代码体积过大,不使用inline,eval
源代码隐藏:
nosources-source-map 全部隐藏
hidden-source-map 只隐藏构建后的源代码,会提示构建后代码错误信息
5.3. oneOf
https://webpack.docschina.org/configuration/module#ruleoneof
提升生产环境构建速度
当规则匹配时,只使用第一个匹配规则
不能有两个配置处理同种类型文件
1 | rules:[ |
5.4. 缓存
js - 对babel进行缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
// js兼容
test: /\.js$/,
// 排除模块文件夹
exclude: /node-modules/,
loader: 'babel-loader',
options: {
presets:[
],
// 开启缓存
// 第二次构建是,会读取之前的缓存
cacheDirectory: true
}
}文件资源缓存
hash值
每次webpack构建时会生成一个唯一的hash值
js和css使用同一个hash值
重新打包时会导致所有缓存失效,即使只改变了一个文件
chunkhash值
也是生成hash值。如果打包来源于同一个chunk,hash值一样
js和css还是使用同一个hash值。因为css来源于js
contenthash
根据文件内容生成hash值
不同文件不同hash值
1
2
3
4
5
6
7
8
9
10output: {
// 文件名
filename: 'js/build.[contenthash:10].js'
},
plugins: [
// 打包css
new MiniCssExtractPlugin({
filename: 'css/main.[contenthash:10].css'
})
]
5.5. tree shaking
去除无用代码
必须使用es6模块化,开启production环境
减少代码
在package.json中设置,
1 | "sideEffects":false; |
所有代码都没有副作用,可以进行tree shaking
但可能导致css 文件被删除
修改为
1 | "sideEffects": ["*.css","*.less"]; |
5.6. code split
设置多入口
1
2
3
4
5
6
7entry: {
index: './src/js/index.js',
test: './src/js/test.js'
},
output: {
filename: 'js/[name].[contenthash:10].js'
}optimization
在webpack.config.js 中搭配多入口
1
2
3
4
5
6
7
8
9/*
1. 可以将 node_modules 中代码单独打包一个 chunk 最终输出
2. 自动分析多入口 chunk 中,有没有公共的文件。如果有会打包成单独一个 chunk
*/
optimization: {
splitChunks: {
chunks: 'all'
}
}动态import
在 webpack.config.js
1
2
3
4
5optimization: {
splitChunks: {
chunks: 'all'
}
}在 src/js/index.js 中添加代码
1
2
3
4
5
6
7
8import(/*webpackChunkName: 'print'*/"./print")
.then(({ print, getNum }) => {
print();
console.log(getNum(3));
})
.catch(() => {
console.log('文件加载失败');
});
出现错误:ERROR in [eslint] error Parsing error: Unexpected token (
1 | npm i @babel/eslint-parser --save-dev |
package.json
1 | "eslintConfig": { |
重新运行
1 | npm run build |
生成单独的 print.071e5f2a63.js文件
5.7. lazy loading
js懒加载
进入页面不会加载print,当点击按钮才会加载print文件
- 第一次加载后,第二次会直接执行,不会重复加载
- 使用时才加载
1 | // src/js/index.js |
webpackPrefetch 预加载
- 使用之前加载js文件
- 正常加载可以认为是并行加载(同时加载多个文件),没有明确的顺序,在前的先加载,同时文件过多过大,影响顺序;而预加载是等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
- 兼容性不好(PC端适用)
1 | document.getElementById("btn").onClick=function(){ |
5.8. pwa
渐进式网络开发应用程序(离线可访问)
https://webpack.docschina.org/guides/progressive-web-application/#root
下载安装包
1
npm install --save-dev workbox-webpack-plugin
修改配置
1
2
3
4
5
6
7
8
9
10
11
12plugins:[
// 离线可访问
new WorkboxWebpackPlugin.GenerateSW({
/*
帮助 serviceworker 快速启动
删除旧的 serviceworker
生成一个 serviceworker 配置文件~
*/
clientsClaim: true,
skipWaiting: true
})
]eslint不认识window、navigator全局变量
解决:需要修改package.json中eslintConfig配置
1
2
3"env": {
"browser": true
}serviceworker代码必须运行在服务器上
启动服务器,将build目录下所有资源作为静态资源暴露出去
1
2npm i serve -g
serve -s build
5.9. 多进程打包
开启多进程打包
https://webpack.docschina.org/loaders/thread-loader/#root
进程启动大概为600ms,进程通信也有开销
只有工作消耗时间比较长,才需要多进程打包
下载安装包
1
npm install --save-dev thread-loader
修改配置
放在需要多进程打包的loader之前
babel-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
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
56module:{
rules:[
{
oneOf: [
{
// js兼容
test: /\.js$/,
// 排除模块文件夹
exclude: /node-modules/,
use: [
{
/*
开启多进程打包。
进程启动大概为 600ms,进程通信也有开销。
只有工作消耗时间比较长,才需要多进程打包
*/
loader: 'thread-loader',
options:{
// 进程2个
work: 2
}
},
{
loader: 'babel-loader',
options: {
presets:[
[
'@babel/preset-env',
{
useBuiltIns:'entry',
corejs:{
version: 3
},
// 兼容浏览器
targets: {
"chrome": "60",
"firefox": "60",
"ie": "6",
"safari": "10",
"edge": "17"
}
}
]
],
// 开启缓存
// 第二次构建是,会读取之前的缓存
cacheDirectory: true
}
}
]
}
]
}
]
}
5.10. externals
拒绝打包,通过http链接引入 http://cdn.bootcss.com/jquery/1.12.4/jquery.min.js
1 | module.exports = { |
5.11. dll
优化重复打包第三方库,加快打包速度
使用dll技术,对某些库(第三方库:jquery、react、vue)进行单独打包
当你运行webpack时,默认查找webpack.config.js配置文件
需要运行webpack.dll.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
27const {resolve} = require("path");
const webpack = require("webpack");
/*
利用dll技术,对某些库(jquery,vue,react)进行单独打包
*/
module.exports={
entry: {
// 最终打包生成的[name]->jquery
// ['jquery'] -> 要打包的库是jquery
jquery: ['jquery']
},
output: {
filename: '[name].js',
path: resolve(__dirname,'dll'),
// 打包的库里面向外暴露出去的内容叫什么名字
library: '[name]_[hash]'
},
plugins: [
// 打包生成一个manifest.json --> 提供和jquery映射
new webpack.DllPlugin({
name: '[name]_[hash]', // 映射库暴露的内容名称
path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
})
],
mode: 'production'
}修改命令
1
npx webpack --config webpack.dll.js
生成文件
dll
|- jquery.js
|- jquery.js.LICENSE
|- manifest.json
安装插件
1
npm i add-asset-html-webpack-plugin --save-dev
修改配置
https://github.com/SimenB/add-asset-html-webpack-plugin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 引入dll
const webpack = require('webpack');
// 打包的dll文件在html中自动引入
const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");
plugins:[
// 告诉webpack哪些代码不需要打包,同时引用的名称也改变
new webpack.DllReferencePlugin({
manifest: resolve(__dirname,'dll/manifest.json')
}),
// 将每个文件打包输出去,并在html中自动引入
new AddAssetHtmlPlugin({
filepath: resolve(__dirname,'dll/jquery.js'),
outputPath: 'libs/dll',
publicPath: 'libs/dll'
})
]引入资源
在src/js/index,js中,引入jquery
1
2
3
4
5
6
7
8
9
10
11
12
13import $ from 'jquery';
$('#btn').on('click', () => {
import(/* webpackChunkName: 'Print',webpackPrefetch: true */'./print')
.then(({ print, getNum }) => {
print();
// eslint-disable-next-line
console.log(getNum(3));
})
.catch(() => {
console.log('文件加载失败');
});
});运行命令
1
npm run build
5.12. 性能优化总结
5.12.1. 开发环境性能优化
- 优化打包构建速度
- HMR
- 优化代码调试
- source-map
5.12.2. 生产环境性能优化
优化打包构建事项
- oneof
- babel缓存
- 多进程打包
- externals
- dll
优化代码运行的性能
- 缓存(hash-chunkhash-contenthash)
- hash:webpack每次打包都会重新生成一个hash
- chunkhash:同属于一个chunk,共用一个hash
- contenthash:根据文件内容生成的hash,文件内容变化才会重新生成hash
- tree shaking
- es6 module
- production环境
- sideEffect参数
- code split 代码分隔
- 单入口
- 多入口
- 懒加载/预加载
- pwa
- 缓存(hash-chunkhash-contenthash)
6. webpack 配置详情
6.1. entry
string
–> ‘./src/index.js’
单入口
打包形成一个chunk,输出一个bundle文件
filename: ‘[name].js’ 此时chunk的名称默认是main
array
–> [‘./src/index.js’, ‘./src/add.js’]多入口
所有入口文件最终只会形成一个chunk,输出出去只有一个bundle文件
–> 只有在HMR功能中让html热更新生效~
object
–> { index: ‘./src/index.js’, add: ‘./src/add.js’ }多入口
有几个入口文件就形成几个chunk,输出几个bundle文件
此时chunk的名称是key
–> 特殊用法
1
2
3
4
5
6{
// 所有入口文件最终只会形成一个chunk,输出出去只有一个bundle文件
index: ['./src/index.js', './src/count.js'],
// 形成一个chunk,输出一个bundle文件
add: './src/add.js'
}
6.2. output
1 | output: { |
chunkFilename 用于代码分离时,动态import生成的外部js文件命名
6.3. resolve
1 | module.exports = { |
index.js
1 | import '../css/index.css' |
出现错误❌:error Unable to resolve path to module '@/style.css' import/no-unresolved
Eslint 无法识别alias别名
https://www.jianshu.com/p/17a67afaf293
安装eslint配置文件 .eslintrc.js
1
2npm init @eslint/config
npm i eslint-import-resolver-alias --save-dev添加配置 .eslintrc.js
https://github.com/johvin/eslint-import-resolver-alias
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const {resolve} = require("path");
module.exports = {
"env": {
"browser": true,
"es6": true,
"node": true,
"jquery": true
},
"parser": "@babel/eslint-parser",
"parserOptions": {
"requireConfigFile": false
},
settings: {
"import/resolver": {
alias: true
}
}
}运行命令
1
npm run build
6.4. dev-server
1 | // devServer |
6.5. optimization
会根据你选择的 mode
来执行不同的优化, 不过所有的优化还是可以手动配置和重写
1 | optimization: { |