01 基础篇,了解 webpack 的相关概念,能够配置 webpack
本篇文章中 webpack 的版本为 5.73.0,cli 版本为 4.10.0。
第一步,安装 webpack 与 webpack-cli。
npm i webpack@5.73.0 webpack-cli@4.10.0第二步,创建 src 文件夹,新建 index.js 作为入口文件,并填充如下内容:
const hello = () => {
console.log('hello, webpack5')
}
hello();第三步,我们直接在终端中输入 npx webpack 看看会发生什么?
可以发现目录中多出了一个 dist 文件夹,这个文件夹就是 webpack 打包输出后的内容。
TIP
从 webpack v4.0.0 开始,webpack 可以不用再引入一个配置文件来打包项目,webpack 会内置一些默认配置供开发者直接使用。
现在我们已跑通了最简单的 webpack 练习环境,接下来我们将结合示例理解 webpack 中 entry,output,loader,plugin,mode 这五大概念。
entry 入口
入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图 (dependency graph)的开始。
我们现在大多数开发的是单页面应用,这里我们以单入口为例,修改目录结构,新增与 src 同级的 webpack.config.js 文件与 add.js 文件。
├─ src
│ ├─ add.js
│ └─ index.js
└─ webpack.config.js
└─ package-lock.json
└─ package.jsonwebpack.config.js 文件配置如下,entry 配置的是相对路径。
module.exports = {
entry: './src/index.js'
}add.js 内容如下:
const add = (a,b) => {
return a + b
}
export default addindex.js 引入这个 add 函数并输出。
import add from './add.js'
const hello = () => {
console.log('hello webpack')
}
hello()
console.log(add(2,3))接下来打开终端输入 npx webpack 回车看看它打包后的文件长什么样。
dist 目录下只有单独的一个 main.js,部分内容如下:

output 输出
可以通过配置 output 选项,告知 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个 entry 起点,但只能指定一个 output 配置。
我们更改 webpack.config.js 的配置如下:
const path = require('path')
const resolvePath = _path => path.resolve(__dirname, _path)
module.exports = {
entry: './src/index.js',
output: {
path: resolvePath('./dist'),
filename: 'scripts/[name].js'
}
}讲解一下 output 中这 2 个属性:
- path:文件输出的路径,必须是个绝对路径
- filename:文件输出的名称。可以在名称前指定一个输出路径,让文件输出在这个路径下。同时也可以使用占位符
[name]指定不同的文件名称
终端输入 npx webpack 打包,我们查看打包后的程序。

新的 main.js 被打包到我们在 filname 中指定的 scripts 文件夹下。
这里有个新的问题待我们解决,之前被打包的 main.js 没有被清除,我们希望每次打包都能生成最新的文件,并清除上一次的打包结果。此时在 output 中配置 clean: true 即可解决问题,更改 output 配置如下:
output: {
path: resolvePath('./dist'),
clean: true,
filename: 'scripts/[name].js'
}再次打包,之前的文件已被清除。

TIP
webpack 5 可以方便地在 output 中配置 clean: true 以清空上一次打包的内容。但在 webpack 4 及一下的版本中,需要借助插件 clean-webpack-plugin 来实现该清除功能。
loader
webpack 天生只知道怎么处理 JavaScript 和 JSON 文件,对于其它类型文件,webpack 不知道怎么处理,如果没有对应 loader,就会报错。
loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。
那什么是 webpack 不能直接处理的资源呢?我们尝试打包一个 css 资源,调整目录结构如下:
├─ src
│ ├─ css
│ │ └─ index.css
│ └─ add.js
│ └─ index.js
└─ webpack.config.js
└─ package-lock.json
└─ package.jsonindex.css 中添加如下样式:
body {
background-color: red;
}index.js 引入 index.css 后,进行打包,结果控制台报错:
import add from './add.js'
import './css/index.css'
const hello = () => {
console.log('hello webpack')
}
hello()
console.log(add(2,3))
这就是 webpack 不知道该怎么处理的资源。
此时就需要我们引入 loader,告诉 webpack 该怎么处理这些资源。
接下来我将围绕样式资源,图片资源,ESNext 等跟大家讲解 loader 的具体使用方法。
样式 loader
告诉 webpack 怎么加载 css 文件的 loader:css-loader,style-loader。
css-loader 的作用可以简单理解为将匹配到的 css 文件的样式内容以字符串的形式拼接在一起,然后将其作为 js 模块的导出内容。
经过 css-loader 的转译,我们已经得到了完整的 css 样式代码,style-loader 的作用就是将结果以 style 标签的方式插入 DOM 树中。
这也就是为什么处理 css 文件要有两个 loader,css-loader 将 css 处理为 js 模块后,将内容交给 style-loader,style-loader 再把 css 插入到 style 标签中。
npm i css-loader style-loader下载完成后,配置 webpack.config.js 使用 loader:
module.exports = {
// ...
module: {
rules:[{
test: /\.css$/,
// 使用 use,达到链式调用的目的
use:[
'style-loader',
'css-loader'
]
}]
}
}这里我们新增了 module 配置项,它用来配置不同 loader 的使用规则。
module.rules 允许你在 webpack 配置中指定多个 loader。
这里的配置意思是,当 webpack 每遇到一个 css 文件时,先用 css-loader 处理,紧接着使用 style-loader 处理。处理完后再去匹配下一个文件。注意 loader 的执行顺序是从下到上,从右到左。先执行 css-loader 处理 css,再执行 style-loader 将 css 通过 style 标签插入页面。
执行 npx webpack,编译成功后,我们在 dist 根目录下新建 index.html,并引入打包后的 main.js 查看结果:

可以发现打包后的 js 和样式全部生效了。
样式通过 style 标签被插入页面。到这里基础的样式 loader 使用就已经成功。但实际开发中我们多以 less,scss 开发为主,处理这些样式文件需要使用对应的 less-loader 和 sass-loader。
less-loader 处理 less 文件
我们调整 src/css 文件结构,新增一个 less 文件,如下所示:
├─ css
│ ├─ index.css
│ └─ rectangle.lessless 文件样式如下:
@blue: #4285f4;
.webpack-rectangle{
width: 100px;
height: 60px;
background-color: @blue;
}src/index.js 引入 less 文件,进行打包。
import add from './add.js'
import './css/index.css'
import './css/rectangle.less'
// ...结果发现报错:

报错信息:不支持 less 文件的打包,需要一个 loader 处理该类文件。
与 css 报错的信息基本一致,这时请出我们的 less-loader。
安装 less,less-loader。
npm i less less-loader配置 webpack.config.js:
// ...
module.exports = {
// ...
module: {
rules:[{
test: /\.css$/,
use:[
'style-loader',
'css-loader'
]
}, {
// 匹配 less 文件
test: /\.less$/,
// loader 的使用顺序 less-loader,css-loader,style-loader
use:[
'style-loader',
'css-loader',
'less-loader'
]
}]
},
mode:'development'
}这里新增 mode:'development',防止 webpack 报一些提示信息的错误。
然后在 rules 数组中新增处理 less 文件的 loader 对象。
这里的处理顺序如下所示:
- less-loader 将 less 文件转义成 css 文件
- 在 1 的基础上,css-loader 将 css 文件处理成 commonJs 模块
- 在 2 的基础上,style-loader 将 css 通过 style 标签插入页面中
最后,在 dist 根目录下再次新建一个 index.html 引入 main.js 查看刚刚写的 less 是否生效,这里为了观察方便我们把原来的红色背景改成灰色背景。
<body>
<div class="webpack-rectangle"></div>
<script src="./scripts/main.js"></script>
</body>ok,生效!大功告成。接下来我们处理 sass,scss 文件。

sass-loader 处理 sass 文件
调整 src/css 文件结构,新增一个 scss 文件,如下所示:
├─ css
│ ├─ index.css
│ └─ circle.scss
│ └─ rectangle.lessscss 文件样式如下:
$baseCls:"webpack-circle";
.#{$baseCls} {
width: 100px;
height: 100px;
border: 1px solid #4285f4;
border-radius: 50%;
}src/index.js 引入 scss 文件,进行打包。
import add from './add.js'
import './css/index.css'
import './css/rectangle.less'
import './css/circle.scss'
// ...下面与处理 less-loader 的方法大同小异。
安装 sass,sass-loader:
npm i sass sass-loader配置 webpack.config.js 的 rules:
rules:[
// ...
{
test: /\.s[ac]ss$/,
use:[
'style-loader',
'css-loader',
'sass-loader'
]
}
]解析规则与 less-loader 相似,在此不多赘述。
最后,执行 npx webpack,dist 根目录下创建 index.html 引入 main.js 查看样式。
<body>
<div class="webpack-rectangle"></div>
<div class="webpack-circle"></div>
<script src="./scripts/main.js"></script>
</body>
TIP
为了看得清楚,把 body 标签的背景设置为了白色。
大功告成,到这里我们的 webpack 就可以正常解析 less 和 sass 了。
如果我们希望样式被抽离成单独的 css 文件以 link 标签的形式引入页面,需要借助插件实现。下一章节 plugin 中我们跟大家进行详细讲解。
资源 loader
webpack4 中处理图片使用 file-loader 和 url-loader,webpack5 中这 2 个 loader 已被内置到 webpack 里,我们只需激活配置即可。
在开始工作之前我们把 src 做个备份,然后继续开发。
调整 src 结构:
├─ src
│ ├─ assets
│ │ └─ changeface.gif
│ │ └─ daxiang.jpg
│ │ └─ putao.png
│ ├─ css
│ │ └─ index.scss
│ └─ add.js
│ └─ index.js配置 webpack.config.js 的 rules,里面的字段含义我们稍后讨论。
rules:[
// ...
{
test: /\.(jpe?g|png|gif|webp|svg)$/,
type: 'asset',
generator: {
filename: 'assets/img/[hash:10][ext]'
}
}
]然后修改 index.scss。
$baseCls:"webpack-img";
.#{$baseCls} {
display: flex;
justify-content: space-between;
div{
width: 33%;
height: 230px;
&.box1{
background: url(../assets/putao.png) no-repeat center /contain;
}
&.box2{
background: url(../assets/changeface.gif) no-repeat center /contain;
}
&.box3{
background: url(../assets/daxiang.jpg) no-repeat center /contain;
}
}
}最后执行 npx webpack,dist 根目录下创建 index.html 引入 main.js 查看样式。
<body>
<div class="webpack-img">
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
</div>
<script src="./scripts/main.js"></script>
</body>查看结果:

成功运行,我们看看 dist 目录生成的文件:

新生成了 assets 文件夹,里面包含 hash 名称的图片资源,再看看刚刚的 loader 中有这样的配置:
generator: {
filename: 'assets/img/[hash:10][ext]'
}这下应该明白了,filename 可以指定图片资源的输出路径,其他字段释义如下:
[hash:10]:取 hash 的前 10 位为文件名称[ext]:文件扩展名,之前是什么值,打包后仍然是什么值
可以看出,打包前和打包后,文件扩展名是未发生修改的。到这里图片资源的处理就完成了。
babel-loader
将 ES6+ 语法编写的代码转义为向下兼容的 js 代码,使低版本浏览器能正常运行程序。
在使用 babel-loader 前,我们备份一下 src,调整目录结构如下:
├─ src
│ └─ index.js
└─ webpack.config.js
└─ package.json在 index.js 中编写一段 ES6 代码:
class GirlFriend {
constructor(name, age) {
this.name = name
this.age = age
}
say () {
console.log(`我叫${this.name},我今年${this.age}岁。很高兴认识你`)
}
}
const girl = new GirlFriend('Alice',22)
girl.say()使用 webpack 打包查看结果:

可以发现,ES6 的代码被原封不动地打包了,这样的打包结果,对低版本浏览器并不友好。所以我们需要引入 babel-loader 解决这个问题,下面我们开始配置 babel-loader。
安装依赖。
npm i babel-loader @babel/core @babel/preset-env- @babel/core:babel 核心库
- @babel/preset-env:智能预设
配置 webpack.config.js 的 rules:
rules: [
// ...
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]当然我们也可以在项目根目录中新增 .babelrc 或 babel.config.js 等文件,设置一些 babel 的使用规则。
然后,执行 webpack 查看打包结果:

可以看到我们的 ES6 代码已被转义为 ES5 代码。

到这里常见 loader 的配置就学习完毕了,接下来我们进入 plugin 的学习。
plugin 插件
plugins 选项用于以各种方式自定义 webpack 构建过程。它监听 webpack 的打包过程,执行对应的生命周期回调,拓展 webpack 的功能。
下面我们来了解最常见的 2 个 plugin。
使用 HtmlWebpackPlugin 打包 html 页面
之前我们打包 js,css 后,总要在 dist 根目录下新建一个 html,引入打包的 js 并进行一些设置,才能在页面中看到打包后的效果。这种方式非常不方便,因此我们通过 HtmlWebpackPlugin 这一插件,让 webpack 帮我们自动打包出一个 index.html。
这里我们提一个需求,我们希望 webpack 能打包一个 html 页面,这个页面生成时带有这样的内容 <h2>Hello CengFan!</h2>,并引入打包后的 js 资源。
需求有了接下来我们一步步实现。
步骤 1,src 文件夹下新建一个 html,内容如下:
<body>
<h2>Hello Cengfan!</h2>
</body>步骤 2,安装依赖。
npm i html-webpack-plugin步骤 3,配置 webpack.config.js。
const path = require('path')
// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const resolvePath = _path => path.resolve(__dirname, _path)
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: resolvePath('./src/index.html'),
})
],
mode: 'development'
}template 指定以哪个 html 为入口文件,值是一个绝对路径。
plugin 都要遵循先引用,然后使用 new 关键字进行实例化调用的原则。
步骤 4,执行 webpack 查看打包结果。
符合预期,js 也正常运行。

使用 MiniCssExtractPlugin 打包独立的 css 文件
还记得之前在样式 loader 一节中我们遇到的问题吗,css 无法被单独打包成文件只能通过 style 标签被插入页面。到这里这个问题就能被解决了。
我们恢复之前备份的资源 loader 文件夹,把它设置成 src,并添加 index.html。下面我们正式开始。
步骤 1,安装插件。
npm i mini-css-extract-plugin步骤 2,配置 webpack.config.js。
// 引入插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
// ...
plugins: [
// ...
new MiniCssExtractPlugin({
filename: 'css/[name].css',
}),
],
mode: 'development'
}这里的 filename 与 output 中的类似,不多赘述。
module.exports = {
//...
module: {
rules: [
//...
{
test: /\.s[ac]ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
]
}
//...
]
},
//...
}由于样式是用 scss 写的,所以我们还需更改与之匹配的 loader。如果不更改对应的 loader,最后样式还是会通过 style 标签被插入页面。
index.scss:
$baseCls:"webpack-img";
.#{$baseCls} {
display: flex;
justify-content: space-between;
div{
width: 33%;
height: 230px;
&.box1{
background: red;
}
&.box2{
background: green;
}
&.box3{
background: blue;
}
}
}步骤 3,配置 html 的内容。
<body>
<div class="webpack-img">
<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>
</div>
<h2>Hello Cengfan</h2>
</body>步骤 4,执行 webpack 查看打包结果。


可以看出样式文件已被单独打包。至此常用 plugin 介绍完毕。
mode 模式
mode 主要分为开发/生产两种环境,不同环境打包出来的代码不同。
- development:开发环境
- production:生产环境
首先看看开发环境下打包的 js 代码:

再看看生产环境下打包的 js 代码:

生产环境下我们还可以配合其他插件,压缩生产环境的代码。
其他配置 devServer
之前我们每次对项目进行修改,都要重新执行 webpack 打包,然后手动开启服务检查打包结果,这是非常不方便的。我们希望 webpack 能自动化处理这些事情,这时就需要使用 devServer 解决这些问题。
步骤 1,安装 webpack-dev-server。
npm i webpack-dev-server步骤 2,配置 webpack.config.js。
//...
module.exports = {
//...
devServer: {
host: 'localhost',
port: 8080,
open: true,
hot: true,
},
mode: 'development'
}步骤 3,终端输入命令 npx webpack serve 查看结果。

此时页面就以 8080 端口被启动起来了。
devServer 启动的开发服务器,存在于内存中,不会往硬盘写入文件,没有输出内容。所以即使我们删除 dist 目录,页面还是会照常运行。
最后
至此 webpack 常用的配置已列举完毕,如果你也跟着一路看下来相信基本的配置应该不成问题了。受限于篇幅,更全的配置还是推荐大家去官网查看。