Webpack学习笔记

学习来源:尚硅谷

学习时间:2022年3月23日

1 简介

1.1 webpack是什么

webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。

在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/...)都会作为模块处理。

它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)。

image-20220323195816751

1.2 五个核心概念

  1. Entry:入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
  2. Output:输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
  3. Loader:Loader 让 webpack 能 够 去 处 理 那 些 非 JavaScript 文 件 (webpack 自身只理解JavaScript)
  4. Plugins: 插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩, 一直到重新定义环境中的变量等。
  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 初体验

2.1 初始化配置

  • 新建项目文件夹/webpack/exp,初始化包管理文件:
1
npm init -y
  • 新建文件夹srcbuild,前者存放要打包的入口文件和各类静态资源,后者存放打包后的文件
  • 下载并安装webpack:包括全局模式和开发模式
1
2
npm i webpack@4.41.6 webpack-cli@3.3.11 -g
npm i webpack@4.41.6 webpack-cli@3.3.11 -D

2.2 编译打包应用

  • 在src下新建文件index.jsdata.json
1
2
3
4
{
"name": "Hongyi",
"age": 12
}
1
2
3
4
5
6
7
8
9
import data from "./data.json"

console.log(data);

function add(x, y) {
return x + y;
}

console.log(add(1, 2));
  • 运行指令:

    • 开发环境指令:

      1
      webpack ./src/index.js -o ./build/built.js --mode=development

      功能:webpack会以./src/index.js为入口文件开始打包,打包后输出到./build/built.js整体打包环境为开发环境

    • 生产环境指令:

      1
      webpack ./src/index.js -o ./build/built.js --mode=production

运行结果:

1
2
3
4
5
6
7
8
9
Hash: 56e483aa30f05a7456dd
Version: webpack 4.41.6
Time: 57ms
Built at: 2022-03-23 8:21:22 ├F10: PM┤
Asset Size Chunks Chunk Names
built.js 5.2 KiB main [emitted] main
Entrypoint main = built.js
[./src/data.json] 42 bytes {main} [built]
[./src/index.js] 732 bytes {main} [built]

打包生成后的built.js文件可以直接执行:

1
node ./build/built.js
1
2
{ name: 'Hongyi', age: 12 }
3
  • 在src下新建页面index.html,引用该打包文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<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>
<!-- 引入打包后资源 -->
<script src="./built.js"></script>
</body>
</html>

执行结果:

image-20220323203308897

结论

  1. webpack能处理js/json资源,不能处理css/img等其他资源
  2. 生产环境比开发环境多一个压缩js代码
  3. 生产环境和开发环境将es6模块化编译成浏览器能够识别的模块化

3 开发环境的基本配置

前言:总项目文件夹为webpack,下面建立各个分项目,例如01exp02csspack等,下载模块时,统一在总项目文件夹下执行npm i命令。这样分项目在找模块时,统一从上级目录,即总项目文件夹下寻找模块node_modules

3.1 创建配置文件

  • 新建项目文件夹webpack/02csspack,并新建srcbuild
  • 02csspack新建webpack的配置文件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
/*
webpack.config.js是webpack的配置文件
作用:指示webpack干哪些活(当运行webpack指令时,会加载里面的配置)
所有构建工具都是基于nodejs平台运行的,模块化默认采用commonjs
*/
// resolve用来拼接绝对路径的方法
const {resolve} = require("path");

module.exports = {
// webpack配置

// 入口起点
entry: "",
// 输出
output: {
// 输出文件名
filename: "",
// 输出路径
// __dirname: 当前文件webpack.config.js的目录绝对路径
path: resolve(__dirname, "build")
},
// Loader的配置
module: {
rules: [
// 详细的loader配置
// 不同类型的文件必须配置不同loader处理
{
// 匹配哪些文件
test: ,
// 使用哪些loader进行处理
use: [
]
}
]
},
// plugins的配置
plugins: [

],
// mode打包模式
mode: "",
}
  • 填写配置信息后,运行下列命令即可完成打包
1
webpack

3.2 打包样式资源

需求:打包cssless样式资源

  • 在src下新建文件index.jsindex.cssindex.less
1
2
3
4
5
6
html, body{
margin: 0;
padding: 0;
height: 100%;
background-color: pink;
}
1
2
3
#title{
color: green;
}
1
2
3
// 引入样式资源
import "./index.css";
import "./index.less";
  • 修改webpack配置文件
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
// resolve用来拼接绝对路径的方法
const {resolve} = require("path");

module.exports = {
// webpack配置

// 入口起点
entry: "./src/index.js",
// 输出
output: {
// 输出文件名
filename: "built.js",
// 输出路径
// __dirname: 当前文件webpack.config.js的目录绝对路径
path: resolve(__dirname, "build")
},
// Loader的配置
module: {
rules: [
// 详细的loader配置
// 不同类型的文件必须配置不同loader处理
{
// 匹配哪些文件
test: /\.css$/,
// 使用哪些loader进行处理
use: [
// use中的loader执行顺序为从下到上
// 创建style标签,将js中的样式资源插入html页面,添加到head中生效
"style-loader",
// 将css文件以字符串的形式变成commonjs加载到js中
"css-loader"
]
},
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
// 将less文件编译成css文件
"less-loader"
]
}
]
},
// plugins的配置
plugins: [

],
// mode模式 development或production
mode: "development",
}
  • 下载打包css和less需要的loader模块(在父项目中运行)
1
npm i css-loader@3.4.2 style-loader@1.1.3 less-loader@5.0.0 -D
1
npm i less@3.11.1 -D
  • 运行打包指令
1
webpack

打包文件built.js的部分内容

image-20220324183746479

  • build下新建页面index.html,引入样式资源
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<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>
<h1 id="title">Hello World</h1>
<script src="./built.js"></script>
</body>
</html>

运行结果

image-20220324183835747

可见样式资源生效,其中<head>标签中插入了解析好的<style>标签:

image-20220324183951204

3.3 打包html资源

  • 按上两节的同样结构创建子项目03htmlpack
  • 新建index.jsindex.html,注意后者并没有显式地引用前者
1
2
3
4
5
function add(x, y) {
return x + y;
}

console.log(add(2, 3));
1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1 id="title">Hello World</h1>
</body>
</html>
  • 创建webpack配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const {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: [

]
},
plugins: [
// html-webpack-plugin
// 插件功能:默认会创建一个空的html文件,自动引入打包输出的所有资源(js/css)
new HtmlWebpackPlugin()
],
mode: "development"
}

打包html资源,需要使用html-webpack-plugin插件:

1
npm i html-webpack-plugin@3.2.0 -D

执行打包命令后,在build目录下会创建好built.jsindex.html,其中,index.html会自动引入built.js,但是没有结构(即没有./src/index.html里的结构内容):

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script type="text/javascript" src="built.js"></script></body>
</html>
  • 修改webpack配置文件,输出有结构的html:
1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
// ...
plugins: [
// html-webpack-plugin
// 插件功能:默认会创建一个空的html文件,自动引入打包输出的所有资源(js/css)
// 需求:需要有结构的hmtl文件
new HtmlWebpackPlugin({
// 复制"./src/index.html",自动引入打包输出的所有资源(js/css)
template: "./src/index.html"
})
]
// ...
}

执行打包命令,输出的html内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<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>
<h1 id="title">Hello World</h1>
<script type="text/javascript" src="built.js"></script></body>
</html>

image-20220325140325065

3.4 打包图片资源

  • 新建子项目04imgpack,新建入口文件index.js、页面index.html和样式文件index.less,并在src下存放三张图片:
1
import "./index.less";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<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 id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<!-- 通过img标签引入图片 -->
<img src="./angular.jpg">
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#box1{
width: 100px;
height: 100px;
background-image: url("./vue.jpg");
background-repeat: no-repeat;
background-size: 100% 100%;
}

#box2{
width: 200px;
height: 200px;
background-image: url("./react.png");
background-repeat: no-repeat;
background-size: 100% 100%;
}

#box3{
width: 300px;
height: 300px;
background-image: url("./angular.jpg");
background-repeat: no-repeat;
background-size: 100% 100%;
}
  • 新建webpack配置文件,说明详见注释
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
const {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: /\.(jpg|png|gif)$/,
// 使用一个loader,可直接使用loader
loader: "url-loader",
options: {
// 图片大小小于8kb(8*1024),会被base64编码处理成字符串的形式
// base64优点:减少请求数量,减轻服务器压力
// 缺点:图片体积会变大,文件请求速度更慢
limit: 8 * 1024,
// 问题:url-loader默认使用es6模块化解析
// 而hmtl-loader引入图片采用commonjs模块化解析
// 解决:关闭url-loader的ex6模块化,采用commonjs解析
esModule: false,
// 给图片进行重命名
// [hash:10]取图片的hash前十位
// [ext]取图片原来的扩展名
name: "[hash:10].[ext]"
}
},
{
test: /\.html$/,
// 处理html的img图片(负责引入图片,从而能够被url-loader处理)
loader: "html-loader"
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
],
mode: "development"
}

打包样式中引入的图片资源,需要下载url-loader,它又依赖file-loader

1
npm i url-loader@3.0.0 file-loader@5.0.2 -D

打包html页面中通过img标签引入的图片资源,需要下载tml-loader

1
npm i html-loader@0.5.5 -D
  • 执行打包指令:在build目录下生成对应的js、html和图片,其中小于8kb的图片不会生成,它直接被base64编码为字符串,供浏览器直接解析为图片

image-20220325145157134

打包产生的index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<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 id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<!-- 通过img标签引入图片 -->
<img src="9a4a32dc0c.jpg">
<script type="text/javascript" src="built.js"></script></body>
</html>

执行结果

image-20220325145255596

image-20220325145452786

3.5 打包其他资源

打包其他资源需要file-loader

  • index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<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>
<span class="iconfont icon-dizhi"></span>
<span class="iconfont icon-hongbao"></span>
<span class="iconfont icon-fanhui"></span>
</body>
</html>
  • webpack配置文件
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
const {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以外的资源)
{
// 排除html/js/css
exclude: /\.(css|html|js)$/,
loader: "file-loader"
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
],
mode: "development"
}

3.6 devServer

devServer用于热打包,自动打包(自动编译,自动打开浏览器,自动刷新浏览器…)

  • 修改webpack配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const {resolve} = require("path");
module.exports = {
// ...
// 开发服务器
devServer: {
// 构建后的项目路径
contentBase: resolve(__dirname, "build"),
// 启动gzip压缩,启动服务器更快
compress: true,
// 端口号
port: 3000,
// 自动打开浏览器
open: true
}
}
  • 下载devServer所需的包:
1
npm i webpack-dev-server@3.10.3 -D
  • 启动devServer指令:
1
npx webpack-dev-server

运行输出后,光标闪烁,并自动打开浏览器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Version: webpack 4.41.6
Time: 800ms
Built at: 2022-03-25 3:35:25 ├F10: PM┤
Asset Size Chunks Chunk Names
13c51607b8.png 74.3 KiB [emitted]
9a4a32dc0c.jpg 12.7 KiB [emitted]
built.js 390 KiB main [emitted] main
index.html 521 bytes [emitted]
Entrypoint main = built.js
[./src/index.js] 22 bytes {main} [built]
[./src/index.less] 611 bytes {main} [built]
+ 27 hidden modules
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = index.html
[../node_modules/html-webpack-plugin/lib/loader.js!./src/index.html] 572 bytes {0} [built]
i 「wdm」: Compiled successfully.

特点

devServer只会在内存中编译打包,不会在硬盘中有任何输出。

说明:例如将build文件夹删除,再执行devServer启动命令,项目下不会有build及其打包后文件的创建,它只是在内存中创建,关闭服务器后又会将这些在内存中的内容删除。

而webpack执行命令后则会严格按照流程,在硬盘中生成相应的打包文件。

3.7 开发环境配置

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
const { resolve } = require('path'); 
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// loader 的配置
{
// 处理 less 资源
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
// 处理 css 资源
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 关闭 es6 模块化
esModule: false,
outputPath: 'imgs'
}
},
{
// 处理 html 中 img 资源
test: /\.html$/,
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
// plugins 的配置
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true
}
};

运行:

1
npx webpa

4 生产环境的基本配置

4.1 css优化

4.1.1 提取css成单独文件

之前的打包操作,是将css样式资源全部打包进了built.js代码中,并由style-loader创建style标签,将样式放到html中。这样会导致:

  1. 打包后的js文件过大
  2. 页面加载时,样式出现屏闪现象

因此打包时,需要将css单独提取出来。

  • 项目结构:

image-20220326135726582

  • 入口文件和css文件和html页面:
1
2
3
4
5
#box1 {
width: 100px;
height: 100px;
background-color: pink;
}
1
2
3
4
5
#box2 {
width: 200px;
height: 200px;
background-color: red;
}
1
2
import "../css/a.css";
import "../css/b.css";
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box1"></div>
<div id="box2"></div>
</body>
</html>
  • 提取css成单独文件,需要下载插件:mini-css-extract-plugin
1
npm i mini-css-extract-plugin@0.9.0 -D
  • webpack配置文件:
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
const {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标签,将样式放到html中
// "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'
}

执行打包命令,生成相应的打包后的资源:

image-20220326135931196

其中,html页面内容:<link href="css/built.css" rel="stylesheet">即引入了打包后单独建立的css资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<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>
<link href="css/built.css" rel="stylesheet"></head>
<body>
<div id="box1"></div>
<div id="box2"></div>
<script type="text/javascript" src="js/built.js"></script></body>
</html>

页面效果

image-20220326140102933

4.1.2 css兼容性处理

  • 修改a.css内容,其中末两行是有兼容性问题的:
1
2
3
4
5
6
7
#box1 {
width: 100px;
height: 100px;
background-color: pink;
display: flex;
backface-visibility: hidden;
}
  • css兼容性处理需要下载插件postcss-loaderpostcss-preset-env
1
npm i postcss-loader@3.0.0 postcss-preset-env@6.7.0 -D
  • 修改webpack配置文件
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
const {resolve} = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 设置nodejs的环境变量
process.env.NODE_ENV = "development";
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
// css兼容性处理
// 帮助postcss找到package.json中browserslist里面的配置
// 通过配置加载指定的css兼容性样式

// 使用loader的默认配置,直接写出loader的名称即可,如下:
// "postcss-loader",
// 当需要修改loader中的某些配置时,使用下面的方式
{
loader: "postcss-loader",
options: {
ident: "postcss",
plugins: () => [
// postcss的插件
require("postcss-preset-env")()
]
}
}
]
}

]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
}),
new MiniCssExtractPlugin({
filename: "css/built.css"
})
],
mode: 'development'
}

注意:打包时,默认采用生产环境对css兼容性问题进行处理,与配置文件中的mode无关。如果要使用开发环境对css处理,采用第5行代码。

  • 在项目根目录的package.json中,添加浏览器兼容版本的代码:
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
{
"name": "webpack_code",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^3.4.2",
"file-loader": "^5.0.2",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.9.0",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^1.1.3",
"url-loader": "^3.0.0",
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
},
// 浏览器兼容列表
"browserslist": {
// 开发环境
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
// 生产环境
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
}
  • 执行打包命令,built.css内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
#box1 {
width: 100px;
height: 100px;
background-color: pink;
display: flex;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
#box2 {
width: 200px;
height: 200px;
background-color: red;
}

4.1.3 css代码压缩

  • 下载压缩插件:optimize-css-assets-webpack-plugin
1
npm i optimize-css-assets-webpack-plugin@5.0.3 -D
  • 引入插件,修改webpack配置文件:
1
2
3
4
5
6
7
8
9
10
11
// 引入插件
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = {
// ...
plugins: [
// ...
// 压缩css
new OptimizeCssAssetsWebpackPlugin()
],
mode: 'development'
}

执行打包命令,压缩后的css代码:

1
#box1{width:100px;height:100px;background-color:pink;display:flex;-webkit-backface-visibility:hidden;backface-visibility:hidden}#box2{width:200px;height:200px;background-color:red}

4.2 js优化

4.2.1 js语法检查

  • 下载所需的包:
1
npm i eslint@6.8.0 eslint-loader@3.0.3 eslint-config-airbnb-base@15.0.0 eslint-plugin-import@2.20.1 -D
  • webpack配置文件
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 {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: [
// 语法检查 eslint-loader eslint
// 注意,只检查自己写的源代码,不检查第三方包的代码
// 设置检查规则:package.json中eslintConfig设置
// airbnb --> eslint-config-airbnb-base eslint eslint-plugin-import
{
test: /\.js$/,
// 语法检查时,排除掉第三方包
exclude: /node_modules/,
loader: "eslint-loader",
options: {
// 自动修复eslint的错误
fix: true
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
],
mode: 'development'
}
  • 修改package.json
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
{
"name": "webpack_code",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^3.4.2",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-loader": "^3.0.3",
"eslint-plugin-import": "^2.20.1",
"file-loader": "^5.0.2",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^1.1.3",
"url-loader": "^3.0.0",
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
},
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
},
"eslintConfig": {
"extends": "airbnb-base"
}
}

4.2.2 js兼容性处理

  • 对js做兼容性处理,需要下载babel相关的包
1
npm i babel-loader @babel/core@ @babel/preset-env -D
  • 问题1:只能转换基本语法,但promise等高级语法则不能转换
    • 解决:对全部js语法做兼容性处理,利用@babel/polyfill
1
npm i @babel/polyfill -D
  • 问题2:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,导致打包后代码体积过大
    • 解决:按需加载,引入core-js
1
npm i core-js
  • 待打包的js文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const add = (x, y) => {
return x+y;
}

console.log(add(1, 1));

const promise = new Promise((resolve) => {
setTimeout(() => {
console.log("定时器执行完毕");
resolve();
}, 1000);
})

console.log(promise);
  • webpack配置文件
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 {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: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
// 预设:指示babel做怎么样的兼容性处理
presets: [
[
"@babel/preset-env",
{
// 按需加载
useBuiltIns: "usage",
// 指定core-js模块版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本额浏览器
targets: {
chrome: "60",
firefox: "60",
ie: "9",
safari: "10",
edge: "17"
}
}
]
]
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
],
mode: 'development'
}

4.2.3 js和html代码压缩

  • 压缩js代码:将mode转换至生产模式即可压缩
  • 压缩html代码:增加HtmlWebpackPlugin配置

待打包的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 我是注释 -->
<h1>Hello World</h1>





<!-- 我是注释 -->
<!-- 我是注释 -->
<!-- 我是注释 -->
<!-- 我是注释 -->
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const add = (x, y) => {
return x+y;
}

console.log(add(1, 1));

const promise = new Promise((resolve) => {
setTimeout(() => {
console.log("定时器执行完毕");
resolve();
}, 1000);
})

console.log(promise);
  • webpack配置文件
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
const {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: [

]
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
// 压缩html代码选项
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
],
// 生产环境会自动压缩js代码
mode: 'production'
}

执行打包命令后产生的打包文件:

1
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Document</title></head><body><h1>Hello World</h1><script type="text/javascript" src="built.js"></script></body></html>
1
!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t){console.log(1+1);const n=new Promise(e=>{setTimeout(()=>{console.log("定时器执行完毕"),e()},1e3)});console.log(n)}]);

4.3 生产环境配置

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
const { resolve } = require('path'); 
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin' );
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 定义 nodejs 环境变量:决定使用 browserslist 的哪个环境
process.env.NODE_ENV = 'production';
// 复用 loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
// 还需要在 package.json 中定义 browserslist
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
];

module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
/*正常来讲,一个文件只能被一个 loader 处理。
当一个文件要被多个 loader 处理,那么一定要指定 loader 执行的先后顺序:
先执行 eslint 再执行 babel */
{
// 在 package.json 中 eslintConfig --> airbnb
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: { fix: true }
},
{
// 处理js
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
// 打包图片
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
// 打包html
test: /\.html$/,
loader: 'html-loader'
},
{
// 其他资源
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
},
plugins: [
new MiniCssExtractPlugin({ filename: 'css/built.css' }),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production'
};

运行:

1
webpack

5 优化配置

  • 开发环境性能优化
    • 优化webpack打包构建速度
    • 优化代码调试功能
  • 生产环境性能优化
    • 优化webpack打包构建速度
    • 优化代码运行性能

5.1 HMR