在?大白话跟你唠明白Webpack(基础篇)
大家好我是来蹭饭,一个会点儿吉他和编曲,绞尽脑汁想傍个富婆的摸鱼大师。
上一篇文章点赞明显已经过了万。
所以今天起,咱们正式挖坑webpack
系列的相关内容。争取用最白的大白话跟你唠明白它到底是个什么玩意儿。
一. 前言
webpack对于每个前端工程师的意义不言而喻,既然如此咱就不多言了。
我们看下这个系列的规划,以及读完后你能学到什么。本系列将分为以下六篇文章全方位解析webpack助你构建系统的知识体系:
-
基础篇——了解webpack的相关概念,能够配置webpack
-
原理篇——剖析plugin从概念到手写到原理
-
原理篇——剖析webpack打包原理,手写小型webpack
本篇为该系列的基础篇
,本系列基于webpack5
讲解相关内容。相关案例代码已全部上传至git,欢迎自取,方便的话欢迎点个star,不胜感激。
下面我们将从相关概念,基础配置等章节来讲解本篇内容,大家坐稳扶好,我们发车!
二. 相关概念
2.1 webpack
webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具
。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles
,它们均为静态资源,用于展示你的内容。
我们结合官网的banner图,用大白话来解读这段定义:
-
webpack是一个打包工具,一个资源中转站。它接收一些文件,经过内部处理后,输出对应的静态资源
-
它从入口文件出发,找到入口依赖的模块,再递归寻找依赖的依赖,直到所有模块被加载进来
-
它把开发者使用方便但浏览器不能直接识别的资源(如less,scss,ES Next等),处理成浏览器可直接识别的资源(css,js等)
打个比方,webpack就像一位厨师,浏览器就像顾客。有一天顾客进饭店点了一盘辣椒炒肉,后厨如果端半斤生猪肉和辣椒给顾客,这单生意肯定要黄。厨师需要把生的食材处理成菜才可上桌。那问题就来了,这盘菜要怎么做?
首先明确要炒的是辣椒炒肉而不是辣子鸡丁,将辣椒炒肉作为入口。找到主要的食材,猪肉,辣椒。——确定入口文件,寻找入口依赖的模块
找到后厨师想了想“吃肉不吃蒜,等于没吃蒜”。于是他拿了一头大蒜,紧跟着又拿了点大葱。——寻找依赖的依赖
接着就是炒制工作了,按顺序放入食材,加入调味料,出锅装盘,这一盘菜就能上桌了。——内部处理后,打包成浏览器可直接识别的资源
这就是webpack,它具体如何处理接收到的文件呢?
本篇中我们仅需知道webpack
依据webpack.config.js
这个配置文件的内容,进行对应的打包处理即可。
webpack.config.js
会根据配置的内容指导webpack的工作。如同厨师的菜谱指导厨师做菜。所以本阶段学习webpack的使用,实际上就是学习webpack.config.js
的配置。下面我们将结合实践,理解与之相关的一些概念。
2.2 基础配置
本章我们会搭建一个webpack的练习环境,结合示例理解webpack.config.js
中的概念。本案例中webpack的版本为:5.73.0,cli版本为:4.9.2
2.2.1 环境搭建
- 步骤1: 创建项目,安装webpack与webpack-cli
yarn add webpack webpack-cli -D
- 步骤2: 创建src文件夹,新建index.js作为入口文件,并填充如下内容
至此我们的环境搭建完成,接下来我们打开终端,在案例中输入webpack看看会发生什么?
目录中多出了一个dist
文件夹,这个文件夹就是webpack打包输出后的内容。
有人可能会疑惑,这菜谱webpack.config.js
配置文件都没有,厨师怎么自己做上菜了?
这里需要注意。从webpack v4.0.0
开始,webpack 可以不用再引入一个配置文件来打包项目,webpack会内置一些默认配置供开发者直接使用。不过实际开发中,因为打包和优化等需求,webpack.config.js
还是需要加入到项目中。
至此我们已跑通了最简单的webpack练习环境,接下来我们将结合示例理解webpack中entry,output,loader,plugin,mode这五大概念。
2.3 entry——入口
入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
我们现在大多数开发的是单页面应用,这里我们以单入口为例,具体配置可去文档中查看
我们修改目录结构,新增webpack.config.js
文件(注意该文件必须与src同级,且不能更改命名。后续我们可以通过更改script脚本命令来打破这些限制。)与add.js
文件。
├─ src
│ ├─ add.js
│ └─ index.js
├─ .gitignore
└─ README.md
└─ webpack.config.js
└─ package.json
webpack.config.js
文件配置如下,entry配置的是相对路径:
module.exports = {
entry: './src/index.js'
}
add.js
内容如下:
const add = (a,b) => {
return a + b
}
export default add
index.js
引入这个add函数并输出:
import add from './add.js'
const hello = () => {
console.log('hello webpack')
}
hello()
console.log(add(2,3))
接下来打开终端输入webpack回车看看它是如何打包文件的。
可以看到刚刚引入的add函数也被打包输出。注意这个add函数输出的形式,webpack帮我们直接输出了函数的运算结果。
只要资源被其他模块引入并使用,webpack就会帮我们打包这些资源。
2.4 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]
指定不同的文件名称
终端输入webpack打包,我们查看打包后的程序
新的main.js被打包到我们在filname中指定的scripts文件夹下。
这里有个新的问题待我们解决,之前被打包的main.js没有被清除,我们希望每次打包都能生成最新的文件,并清除上一次的打包结果。此时在output中配置clean:true
即可解决问题,更改output配置如下:
output: {
path: resolvePath('./dist'),
clean: true,
filename: 'scripts/[name].js'
}
再次打包:
效果实现,之前的文件已被清除。这里需要注意,webpack 5.0以下的版本,需要另外引入插件clean-webpack-plugin
进行配置,过程较为复杂,不展开赘述,了解即可。webpack 5.0以上仅需在output中配置clean的值为true即可实现同样的效果。
2.5 loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
我们用大白话翻译一下就是:loader是一个翻译,把webpack不能直接处理的资源,翻译成能直接处理的。
那什么是webpack不能直接处理的资源呢?我们尝试打包一个css资源,调整目录结构如下:
├─ src
│ ├─ css
│ │ └─ index.css
│ └─ add.js
│ └─ index.js
├─ .gitignore
└─ README.md
└─ webpack.config.js
└─ package.json
index.css中添加如下样式:
body {
background-color: red;
}
index.js引入index.css后,进行打包,查看结果如下:
报错提示: webpack不识别样式的写法,我们需要一个合适的loader来处理这种类型的文件。
这就是webpack不能直接处理的资源。也就是说除了webpack开箱自带支持的JS,JSON文件,其余文件都不能被webpack直接处理。此时就需要我们引入loader帮助webpack翻译翻译,什么TMD叫惊喜。
接下来我将围绕样式资源,图片资源,ES Next等跟大家讲解loader的具体使用方法。
2.5.1 样式loader
加载css文件——css-loader,style-loader
- 步骤1: 识别基础css资源需要安装2个loader
yarn add css-loader style-loader -D
loader作用如下:
css-loader | style-loader |
---|---|
将css文件处理成commonJs模块放入js中 | 创建style标签将js中的css插入到html中 |
- 步骤2: 下载完成后,配置
webpack.config.js
使用loader
module.exports = {
// ...
module: {
rules:[{
test: /\.css$/,
use:[
'style-loader',
'css-loader'
]
}]
}
}
这里我们新增了module配置项,它用来配置不同loader的使用规则。module.rules
允许你在 webpack 配置中指定多个 loader。
rules中每一个对象即为处理一种类型文件的方式,具体字段释义如下:
test | use |
---|---|
loader匹配的规则 | 对应文件使用的loader |
这里的配置是为所有css文件匹配使用style-loader
和css-loader
。注意loader的执行顺序是从下到上,从右到左。先执行css-loader
处理css,再执行style-loader
将css通过style标签插入页面。
- 步骤3: 执行webpack查看结果:
编译成功,我们在dist根目录下新建index.html,并引入打包后的main.js查看结果:
打包后的js和样式全部生效了,我们看看样式插入的方式:
样式通过style标签被插入页面。到这里基础的样式loader使用就已经成功。但实际开发中我们多以less,scss开发为主,处理这些样式文件需要使用对应的less-loader
和sass-loader
。
加载less文件——less-loader
我们调整src/css文件结构,新增一个less文件,如下所示:
├─ css
│ ├─ index.css
│ └─ rectangle.less
less文件样式如下:
@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。
- 步骤1: 安装less,less-loader
yarn add less less-loader -D
- 步骤2: 配置
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文件
- css-loader将css文件处理成commonJs模块
- style-loader将css通过style标签插入页面中
- 步骤3: 在dist根目录下再次新建一个index.html引入main.js查看刚刚写的less是否生效,这里为了观察方便我们把原来的红色背景改成灰色背景。
<body>
<div class="webpack-rectangle"></div>
<script src="./scripts/main.js"></script>
</body>
ok生效!大功告成。接下来我们处理sass,scss文件。
加载sass文件——sass-loader
调整src/css文件结构,新增一个scss文件,如下所示:
├─ css
│ ├─ index.css
│ └─ circle.scss
│ └─ rectangle.less
scss文件样式如下:
$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的方法大同小异
- 步骤1: 安装sass,sass-loader
yarn add sass sass-loader -D
- 步骤2: 配置
webpack.config.js
的rules:
rules:[
// ...
{
test: /\.s[ac]ss$/,
use:[
'style-loader',
'css-loader',
'sass-loader'
]
}]
解析规则与less-loader相似,在此不多赘述。
- 步骤3: 执行webpack,dist根目录下创建index.html引入main.js查看样式
<body>
<div class="webpack-rectangle"></div>
<div class="webpack-circle"></div>
<script src="./scripts/main.js"></script>
</body>
大功告成,到这里我们的webpack就可以正常解析less和sass了。
如果我们希望样式被抽离成单独的css文件以link标签的形式引入页面,需要借助插件实现。下一章节plugin中我们跟大家进行详细讲解。
2.5.2 资源loader
处理图片资源
webpack4中处理图片使用file-loader
和url-loader
,webpack5中,这2个loader已被内置到webpack里,我们只需激活配置即可。
在开始工作之前我们把src做个备份,然后继续开发。
- 步骤1: 调整src结构
├─ src
│ ├─ assets
│ │ └─ img.png
│ │ └─ iron-man.gif
│ │ └─ mk50.jpeg
│ ├─ css
│ │ └─ index.scss
│ └─ add.js
│ └─ index.js
- 步骤2: 配置
webpack.config.js
的rules,里面的字段含义我们稍后讨论
rules:[
// ...
{
test: /\.(jpe?g|png|gif|webp|svg)$/,
type: 'asset',
generator: {
filename: 'assets/img/[hash:10][ext]'
}
}]
- 步骤3: 修改index.scss
$baseCls:"webpack-img";
.#{$baseCls} {
display: flex;
justify-content: space-between;
div{
width: 230px;
height: 230px;
&.box1{
background: url(../assets/img/img.png) no-repeat center /contain;
}
&.box2{
background: url(../assets/img/iron-man.gif) no-repeat center /contain;
}
&.box3{
background: url(../assets/img/mk50.jpeg) no-repeat center /contain;
}
}
}
- 步骤4: 执行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] | [ext] |
---|---|
取hash的前10位为文件名称 | 文件扩展名,之前是什么值,打包后仍然是什么值 |
可以看出,打包前和打包后,文件扩展名是未发生修改的。到这里图片资源的处理就完成了。
2.5.3 babel-loader
将 ES6+ 语法编写的代码转义为向下兼容的js代码,使低版本浏览器能正常运行程序。
在使用babel-loader前,我们备份一下src,调整目录结构如下:
├─ src
│ └─ index.js
├─ .gitignore
└─ README.md
└─ 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。
- 步骤1: 安装依赖
yarn add babel-loader @babel/core @babel/preset-env -D
@babel/core | @babel/preset-env |
---|---|
babel核心库 | 智能预设,允许开发者使用最新的js |
- 步骤2: 配置
webpack.config.js
的rules:
rules: [
// ...
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}]
当然我们也可以在项目根目录中新增.babelrc
或babel.config.js
等文件,设置一些babel的使用规则。关于babel更详细地使用这里不多做展开,请查阅官方文档了解。
- 步骤3: 执行webpack查看打包结果
可以看到我们的ES6代码已被转义为ES5代码。
到这里常见loader的配置就学习完毕了,接下来我们进入plugin的学习。
2.6 plugin——插件
plugins
选项用于以各种方式自定义 webpack 构建过程。它监听webpack的打包过程,执行对应的生命周期回调,拓展webpack的功能。
下面我们来了解最常见的2个plugin
2.6.1 打包html页面——HtmlWebpackPlugin
之前我们打包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: 安装依赖
yarn add html-webpack-plugin -D
- 步骤2: 配置
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
关键字进行实例化调用的原则。
- 步骤3: 执行webpack查看打包结果
符合预期,js也正常运行
2.6.2 打包独立的css文件——MiniCssExtractPlugin
还记得之前在样式loader一节中我们遇到的问题吗,css无法被单独打包成文件只能通过style标签被插入页面。到这里这个问题就能被解决了。
我们恢复之前备份的资源loader文件夹,把它设置成src,并添加index.html。下面我们正式开始。
- 步骤1: 安装插件
yarn add mini-css-extract-plugin -D
- 步骤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标签被插入页面。
- 步骤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介绍完毕。
2.7 mode模式
mode主要分为开发/生产两种环境,不同环境打包出来的代码不同。
development | production |
---|---|
开发环境 | 生产环境 |
首先看看开发环境下打包的js代码:
再看看生产环境下打包的js代码:
生产环境下我们还可以配合其他插件,压缩生产环境的代码。
2.8 其他配置
2.8.1 devServer
之前我们每次对项目进行修改,都要重新执行webpack打包,然后手动开启服务检查打包结果,这是非常不方便的。我们希望webpack能自动化处理这些事情,这时就需要使用devServer
解决这些问题。
- 步骤1: 安装
webpack-dev-server
yarn add webpack-dev-server -D
- 步骤2: 配置
webpack.config.js
//...
module.exports = {
//...
devServer: {
host: 'localhost',
port: 8080,
open: true,
hot: true,
},
mode: 'development'
}
devServer的字段释义如下:
host | port | open | hot |
---|---|---|---|
服务器名称 | 端口号 | 是否自动打开浏览器 | 是否开启热更新 |
- 步骤3: 终端输入命令
webpack serve
查看结果
此时页面就以8080端口被启动起来了。
devServer启动的开发服务器,存在于内存中,不会往硬盘写入文件,没有输出内容。所以即使我们删除dist目录,页面还是会照常运行。
三. 尾巴
至此webpack常用的配置已列举完毕,如果你也跟着一路看下来相信基本的配置应该不成问题了。受限于篇幅,更全的配置还是推荐大家去官网查看。
后续的进阶篇,实战篇,原理篇已经在新建文件夹了,希望能尽快跟大家见面。
不知道通篇的大白话是否助你入门了webpack,如果有帮助,希望大家点赞,评论,收藏,你们的支持是我创作的最大动力。
我是来蹭饭,一个会点儿吉他和编曲,绞尽脑汁想傍个富婆的摸鱼大师,希望本次的分享对你有帮助。

转载自:https://juejin.cn/post/7108639298424930335