前言

为什么需要打包工具?

  • 开发时,我们会使用框架(React、Vue),ES6 模块化语法,Less/Sass 等 css 预处理器等语法进行开发。这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、css 等语法,才能运行。所以我们需要打包工具帮我们做完这些事。

  • 除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。

有哪些打包工具?

  • Grunt
  • Gulp
  • Parcel
  • Webpack
  • Rollup
  • Vite

基本使用

什么是webpack?

Webpack 是一个静态资源打包工具。它会以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去。输出的文件就是编译好的文件,就可以在浏览器段运行了。我们将 Webpack 输出的文件叫做 bundle

功能介绍

Webpack 本身功能是有限的:

  • 开发模式:仅能编译 JS 中的 ES Module 语法
  • 生产模式:能编译 JS 中的 ES Module 语法,还能压缩 JS 代码

开始使用(demo)

资源目录

1
2
3
4
5
6
webpack_project # 项目根目录(所有指令必须在这个目录运行)
└── src # 项目源码目录
├── js # js文件目录
│ ├── count.js
│ └── sum.js
└── main.js # 项目主文件

创建文件

  • count.js
1
2
3
export default function count(x, y) {
return x - y;
}
  • sum.js
1
2
3
export default function sum(...args) {
return args.reduce((p, c) => p + c, 0);
}
  • main.js
1
2
3
4
5
import count from "./js/count";
import sum from "./js/sum";

console.log(count(2, 1));
console.log(sum(1, 2, 3, 4));

下载依赖

打开终端,来到项目根目录。运行以下指令:

1
npm init -y 

这条指令用于初始化package.json。此时会生成一个基础的 package.json 文件。

需要注意的是 package.jsonname 字段不能叫做 webpack, 否则下一步会报错

1
npm i webpack webpack-cli -D

启用 Webpack

  • 开发模式
1
npx webpack ./src/main.js --mode=development
  • 生产模式
1
npx webpack ./src/main.js --mode=production

npx webpack: 是用来运行本地安装 Webpack 包的。

./src/main.js: 指定 Webpackmain.js 文件开始打包,不但会打包 main.js,还会将其依赖也一起打包进来。

--mode=xxx:指定模式(环境)。

输出文件

默认 Webpack 会将文件打包输出到 dist 目录下

开发模式介绍

开发模式顾名思义就是我们开发代码时使用的模式。

这个模式下我们主要做两件事:

  1. 编译代码,使浏览器能识别运行

开发时我们有样式资源、字体图标、图片资源、html 资源等,webpack 默认都不能处理这些资源,所以我们要加载配置来编译这些资源

  1. 代码质量检查,树立代码规范

提前检查代码的一些隐患,让代码运行时能更加健壮。

提前检查代码规范和格式,统一团队编码风格,让代码更优雅美观。

处理样式资源

介绍

Webpack 本身是不能识别样式资源的,所以我们需要借助 Loader 来帮助 Webpack 解析样式资源。我们找 Loader 都应该去官方文档中找到对应的 Loader,然后使用。官方文档找不到的话,可以从社区 Github 中搜索查询。

处理CSS资源

1. 下载包
1
npm i css-loader style-loader -D

注意:需要下载两个 loader

2. 功能介绍
  • css-loader:负责将 Css 文件编译成 Webpack 能识别的模块
  • style-loader:会动态创建一个 Style 标签,里面放置 Webpack 中 Css 模块内容

此时样式就会以 Style 标签的形式在页面上生效

3.配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const path = require("path");

module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "main.js",
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: ["style-loader", "css-loader"],
},
],
},
plugins: [],
mode: "development",
};
4.添加CSS资源
  • src/css/index.css
1
2
3
4
5
.box1 {
width: 100px;
height: 100px;
background-color: pink;
}
  • src/main.js
1
2
3
4
5
6
7
import count from "./js/count";
import sum from "./js/sum";
// 引入 Css 资源,Webpack才会对其打包
import "./css/index.css";

console.log(count(2, 1));
console.log(sum(1, 2, 3, 4));
  • public/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!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>webpack5</title>
</head>
<body>
<h1>Hello Webpack5</h1>
<!-- 准备一个使用样式的 DOM 容器 -->
<div class="box1"></div>
<!-- 引入打包后的js文件,才能看到效果 -->
<script src="../dist/main.js"></script>
</body>
</html>
5.运行指令
1
npx webpack

处理Less、Sass、Scss、Styl资源类似

处理图片资源

在 Webpack4 中,处理图片资源通过 file-loaderurl-loader

Webpack5 已经将两个 Loader 功能内置到 Webpack 里了,故只需要简单配置即可处理图片资源

1.配置
1
2
3
4
5
//在webpack.config.js的module.rules中配置
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
},
2.使用图片资源
3.运行命令
1
npx webpack
4.输出资源情况

此时如果查看 dist 目录的话,会发现多了图片资源

因为 Webpack 会将所有打包好的资源输出到 dist 目录下

  • 为什么样式资源没有呢?

因为经过 style-loader 的处理,样式资源打包到 main.js 里面去了,所以没有额外输出出来

5.图片优化

将小于某个大小的图片转化成 data URI 形式(Base64 格式)

1
2
3
4
5
6
7
8
9
10
//在webpack.config.js的module.rules中配置
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
}
}
},
  • 优点:减少请求数量
  • 缺点:体积变得更大

此时输出的图片文件就大于10kb,小于10kb的图片文件以 data URI 形式内置到 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
const path = require("path");

module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /\.s[ac]ss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.styl$/,
use: ["style-loader", "css-loader", "stylus-loader"],
},
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
},
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: "static/imgs/[hash:8][ext][query]",
},
},
],
},
plugins: [],
mode: "development",
};

自动清空上次打包资源

1
2
3
4
5
6
//在webpack.config.js的module.exports中的output中配置
output: {
path: path.resolve(__dirname, "dist"),
filename: "static/js/main.js",
clean: true, // 自动将上次打包目录资源清空
}

处理JS资源

为什么要处理JS资源?

  • 原因是 Webpack 对 js 处理是有限的,只能编译 js 中 ES 模块化语法,不能编译其他语法,导致 js 不能在 IE 等浏览器运行,所以我们希望做一些兼容性处理。

  • 其次开发中,团队对代码格式是有严格要求的,我们不能由肉眼去检测代码格式,需要使用专业的工具来检测。

    • 针对 js 兼容性处理,我们使用 Babel 来完成

    • 针对代码格式,我们使用 Eslint 来完成

  • 我们先完成 Eslint,检测代码格式无误后,在由 Babel 做代码兼容性处理

eslint

  • 可组装的 JavaScript 和 JSX 检查工具。这句话意思就是:它是用来检测 js 和 jsx 语法的工具,可以配置各项功能

  • 使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,将来运行 Eslint 时就会以写的规则对代码进行检查

  1. 配置文件

配置文件由很多种写法:

  • .eslintrc.*:新建文件,位于项目根目录
    • .eslintrc
    • .eslintrc.js
    • .eslintrc.json
    • 区别在于配置格式不一样
  • package.jsoneslintConfig:不需要创建文件,在原有文件基础上写

ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可

  1. 具体配置

我们以 .eslintrc.js 配置文件为例:

1
2
3
4
5
6
7
8
9
10
module.exports = {
// 解析选项
parserOptions: {},
// 具体检查规则
rules: {},
// 继承其他规则
extends: [],
// ...
// 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};
  1. parserOptions 解析选项
1
2
3
4
5
6
7
parserOptions: {
ecmaVersion: 6, // ES 语法版本
sourceType: "module", // ES 模块化
ecmaFeatures: { // ES 其他特性
jsx: true // 如果是 React 项目,就需要开启 jsx 语法
}
}
  1. rules 具体规则
  • "off"0 - 关闭规则
  • "warn"1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  • "error"2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
1
2
3
4
5
6
7
8
9
10
11
12
rules: {
semi: "error", // 禁止使用分号
'array-callback-return': 'warn', // 强制数组方法的回调函数中有 return 语句,否则警告
'default-case': [
'warn', // 要求 switch 语句中有 default 分支,否则警告
{ commentPattern: '^no default$' } // 允许在最后注释 no default, 就不会有警告了
],
eqeqeq: [
'warn', // 强制使用 === 和 !==,否则警告
'smart' // https://eslint.bootcss.com/docs/rules/eqeqeq#smart 除了少数情况下不会有警告
],
}

更多规则详见:规则文档

  1. extends 继承

开发中一点点写 rules 规则太费劲了,所以有更好的办法,继承现有的规则。

现有以下较为有名的规则:

1
2
3
4
5
6
7
8
9
// 例如在React项目中,我们可以这样写配置
module.exports = {
extends: ["react-app"],
rules: {
// 我们的规则会覆盖掉react-app的规则
// 所以想要修改规则直接改就是了
eqeqeq: ["warn", "smart"],
},
};
  1. 在webpack中使用

  2. 下载包

1
npm i eslint-webpack-plugin eslint -D
  1. 定义 Eslint 配置文件
  • .eslintrc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
// 继承 Eslint 规则
extends: ["eslint:recommended"],
env: {
node: true, // 启用node中全局变量
browser: true, // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6,
sourceType: "module",
},
rules: {
"no-var": 2, // 不能使用 var 定义变量
},
};
  1. 修改 js 文件代码
  • main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import count from "./js/count";
import sum from "./js/sum";
// 引入资源,Webpack才会对其打包
import "./css/iconfont.css";
import "./css/index.css";
import "./less/index.less";
import "./sass/index.sass";
import "./sass/index.scss";
import "./styl/index.styl";

var result1 = count(2, 1);
console.log(result1);
var result2 = sum(1, 2, 3, 4);
console.log(result2);
  1. 配置
  • 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
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");

module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
clean: true, // 自动将上次打包目录资源清空
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /\.s[ac]ss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.styl$/,
use: ["style-loader", "css-loader", "stylus-loader"],
},
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
},
},
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: "static/imgs/[hash:8][ext][query]",
},
},
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
],
},
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src"),
}),
],
mode: "development",
};
  1. 运行指令
1
npx webpack

babel

JavaScript 编译器。主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中.

  1. 配置文件

配置文件由很多种写法:

  • babel.config.*:新建文件,位于项目根目录
    • babel.config.js
    • babel.config.json
  • .babelrc.*:新建文件,位于项目根目录
    • .babelrc
    • .babelrc.js
    • .babelrc.json
  • package.jsonbabel:不需要创建文件,在原有文件基础上写

Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可

  1. 具体配置

我们以 babel.config.js 配置文件为例:

1
2
3
4
module.exports = {
// 预设
presets: [],
};

presets 预设。简单理解:就是一组 Babel 插件, 扩展 Babel 功能

  • @babel/preset-env: 一个智能预设,允许您使用最新的 JavaScript。
  • @babel/preset-react:一个用来编译 React jsx 语法的预设
  • @babel/preset-typescript:一个用来编译 TypeScript 语法的预设
  1. 在webpack中使用

  2. 下载包

1
npm i babel-loader @babel/core @babel/preset-env -D
  1. 定义 Babel 配置文件
  • babel.config.js
1
2
3
module.exports = {
presets: ["@babel/preset-env"],
};
  1. 修改 js 文件代码
  • main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import count from "./js/count";
import sum from "./js/sum";
// 引入资源,Webpack才会对其打包
import "./css/iconfont.css";
import "./css/index.css";
import "./less/index.less";
import "./sass/index.sass";
import "./sass/index.scss";
import "./styl/index.styl";

const result1 = count(2, 1);
console.log(result1);
const result2 = sum(1, 2, 3, 4);
console.log(result2);
  1. 配置
1
2
3
4
5
6
//在webpack.config.js的module.exports的module的rules中配置
{
test: /\.js$/,
exclude: /node_modules/, //排除node_modules代码不编译
loader: "babel-loader",
},
  1. 运行指令
1
npx webpack

处理HTML资源

为什么要处理HTML资源?

简化HTML文件的创建,处理HTML资源使得可以自动引入打包生成的JS等资源。

步骤

  1. 下载包
1
npm i html-webpack-plugin -D
  1. 配置
1
2
3
4
5
6
7
//在webpack.config.js的module.exports的plugin中添加
const HtmlWebpackPlugin = require("html-webpack-plugin");
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "public/index.html"),
}),
  1. 删除index.html中引入的JS文件, HtmlWebpackPlugin 会自动引入

开发服务器&自动化

为什么要自动化?

每次写完代码都需要手动输入指令才能编译代码,太麻烦了,我们希望一切自动化

步骤

  1. 下载包
1
npm i webpack-dev-server -D
  1. 配置
1
2
3
4
5
6
7
// 在webpack.config.js的module.exports中配置
// 开发服务器
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
},
  1. 运行指令
1
npx webpack serve

!注意点

当使用开发服务器时,所有代码都会在内存中编译打包,并不会输出到 dist 目录下。因为开发时只关心代码能运行,有效果即可,至于代码被编译成什么样子,并不需要知道。

生产模式

生产模式是开发完成代码后,我们需要得到代码将来部署上线。

这个模式下我们主要对代码进行优化,让其运行性能更好。

优化主要从两个角度出发:

  1. 优化代码运行性能
  2. 优化代码打包速度

生产模式-CSS处理

提取CSS成单独文件

Css 文件目前被打包到 js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式

网站会先处理JS,再处理CSS。在网速慢的情况下,会出现闪屏现象,用户体验不好

我们应该是单独的 Css 文件,通过 link 标签加载性能才好

步骤

  1. 下载包
1
npm i mini-css-extract-plugin -D
  1. 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 在webpack.config.js的module.exports的module.rules和plugin中配置
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: [MiniCssExtractPlugin.loader, "css-loader"],
}]
plugins: [
// 提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: "static/css/main.css",
}),
]

CSS兼容性处理

  1. 下载包
  2. 配置
  3. 控制兼容性

我们可以在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度。

1
2
3
4
{
// 其他省略
"browserslist": ["ie >= 8"]
}

想要知道更多的 browserslist 配置,查看browserslist 文档

以上为了测试兼容性所以设置兼容浏览器 ie8 以上。

实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:

1
2
3
4
{
// 其他省略
"browserslist": ["last 2 version", "> 1%", "not dead"]
}

CSS压缩

  1. 下载包

    optimize-css-assets-webpack-plugin

  2. 配置

HTML压缩

默认生产模式已经开启了:html 压缩和 js 压缩,不需要额外进行配置

总结

  1. 两种开发模式
  • 开发模式:代码能编译自动化运行
  • 生产模式:代码编译优化输出
  1. Webpack 基本功能
  • 开发模式:可以编译 ES Module 语法
  • 生产模式:可以编译 ES Module 语法,压缩 js 代码
  1. Webpack 配置文件
  • 5 个核心概念
    • entry:打包入口
    • output:输出文件的地方
    • loader:模块转换器
    • plugins:扩展插件
    • mode:模式,开发模式或生产模式
  • devServer 配置
  1. Webpack 脚本指令用法
  • webpack 直接打包输出
  • webpack serve 启动开发服务器,内存编译打包没有输出