webpack5 基础入门
前言
为什么需要打包工具?
开发时,我们会使用框架(React、Vue),ES6 模块化语法,Less/Sass 等 css 预处理器等语法进行开发。
这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、CSS 等语法,才能运行。
所以我们需要打包工具帮我们做完这些事。
除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。
为什么需要 Webpack
想要理解为什么要使用 webpack,我们先回顾下历史,在打包工具出现之前,我们 是如何在 web 中使用 JavaScript 的。在浏览器中运行 JavaScript 有两种方法:
第一种方式,引用一些脚本来存放每个功能,比如下面这个文档:
<!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>
<!-- HTML 代码 -->
<div>我的HTML代码</div>
<!-- 引入外部的 JavaScript 文件 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.2/js/bootstrap.min.js"></script>
<!-- 引入我自己的 JavaScript 文件 -->
<script src="./scripts/common.js"></script>
<script src="./scripts/user.js"></script>
<script src="./scripts/authentication.js"></script>
<script src="./scripts/product.js"></script>
<script src="./scripts/ .js"></script>
<script src="./scripts/payment.js"></script>
<script src="./scripts/checkout.js"></script>
<script src="./scripts/shipping.js"></script>
</body>
</html>此解决方案很难扩展,因为加载太多脚本会导致网络瓶颈。同时如果你不小心更改了 JavaScript 文件的加载顺序,这个项目可能要崩溃。
第二种方式,使用一个包含所有项目代码的大型 .js 文件, 对上面的文档做改进:
<!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>
<!-- HTML 代码 -->
<div>我的HTML代码</div>
<!-- 引入我自己的 JavaScript 文件 -->
<script src="./scripts/bundle.33520ba89e.js"></script>
</body>
</html>这种方式解决了方式一的问题,但会导致作用域、文件大小、可读性和可维护性方面的问题。
如何解决作用域问题
早先前,我们使用 Grunt 和 Gulp 两个工具来管理我们项目的资源。
这两个工具称为任务执行器,它们将所有项目文件拼接在一起。利用了 立即调用函数 表达式(IIFE) - Immediately invoked function expressions , 解决了大型项目 的作用域问题;当脚本文件被封装在 IIFE 内部时,你可以安全地拼接或安全地组合所有文件,而不必担心作用域冲突。
什么是 IIFE,参见下面的代码:
当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问。
javascriptfunction () { var name = "Barry"; })(); // 无法从外部访问变量 name name // 抛出错误:"Uncaught ReferenceError: name is not defined"将 IIFE 分配给一个变量,不是存储 IIFE 本身,而是存储 IIFE 执行后返回的 结果。
var result = (function () {
var name = 'Barry'
return name
})()
// IIFE 执行后返回的结果:
result // "Barry"Grunt , Gulp 解决了作用域问题。但是,修改一个文件意味着必须重新构建整个文件。拼接可以做到很容易地跨文件重用脚本,却使构建结果的优化变得更加困难。如何判断代码是否实际被使用?
即使你只用到 lodash 中的某个函数,也必须在构建结果中加入整个库,然后将它们压缩在一起。大规模地实现延迟加载代码块及无用代码的去除,需要开发人员手动地进行大量工作。
<!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>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
</head>
<body>
<script>
const str = _.join(['千锋大前端教研院', 'Webpack5学习指南'], '-')
console.log(str)
</script>
</body>
</html>如何解决代码拆分问题
感谢 Node.js ,JavaScript 模块诞生了!
Node.js 是一个 JavaScript 运行时,可以在浏览器环境之外的计算机和服务器中使 用。webpack 运行在 Node.js 中。
当 Node.js 发布时,一个新的时代开始了,它带来了新的挑战。既然不是在浏览器中 运行 JavaScript,现在已经没有了可以添加到浏览器中的 html 文件和 script 标签。 那么 Node.js 应用程序要如何加载新的代码文件呢?
CommonJS 问世并引入了 require 机制,它允许你在当前文件中加载和使用某个模块。导入需要的每个模块,这一开箱即用的功能,帮助我们解决了代码拆分的问题。
Node.js 已经成为一种语言、一个平台和一种快速开发和创建快速应用程序的方式,接管了整个 JavaScript 世界。
但是CommonJS 没有浏览器支持。没有 live binding(实时绑定)。循环引用存在问题。 同步执行的模块解析加载器速度很慢。虽然 CommonJS 是 Node.js 项目的绝佳解决方案,但浏览器不支持模块,我们似乎又遇到了新问题。
如何让浏览器支持模块
在早期,我们应用 Browserify 和RequireJS等打包工具编写能够在浏览器中运行的 CommonJS 模块:
目前,我们还有一个选择,就是来自 Web 项目的好消息是,模块正在成为 ECMAScript 标准的官方功能。然而,浏览器支持不完整,版本迭代速度也不够快,还是推荐上面两个早期模块实现。早期的任务构建工具基于 Google 的 Closure 编译 器,要求我们手动在顶部声明所有的依赖,开发体验不好。
Webpack 搞定这一切
是否可以有一种方式,不仅可以让我们编写模块,而且还支持任何模块格式(至少在 我们到达 ESM 之前),并且可以同时处理resource和 assets ?
这就是 webpack 存在的原因。它是一个工具,可以打包你的 JavaScript 应用程序 (支持 ESM 和 CommonJS),可以扩展为支持许多不同的静态资源,例如: images , fonts 和 stylesheets 。
webpack 关心性能和加载时间;它始终在改进或添加新功能,例如:异步地加载和 预先加载代码文件,以便为你的项目和用户提供最佳体验。
有哪些打包工具?
- Grunt
- Gulp
- Parcel
- Webpack
- Rollup
- Vite
- ...
基本使用
**Webpack 是一个静态资源打包工具。**它会以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去。输出的文件就是编译好的文件,就可以在浏览器段运行了。我们将 Webpack 输出的文件叫做 bundle。
- webpack 是前端的一个资源构建工具,一个静态模块打包器;
- 在 webpack 看来,前端的所有资源文件(js/json/css/less/scss/img...)都是一个个模块;
- webpack 会根据资源的依赖关系生成一个依赖关系图,再打包成对应的静态资源 bundle。
功能介绍
Webpack 本身功能是有限的:
- 开发模式:仅能编译 JS 中的
ES Module语法 - 生产模式:能编译 JS 中的
ES Module语法,还能压缩 JS 代码
开始使用
1. 资源目录
webpack_code # 项目根目录(所有指令必须在这个目录运行)
└── src # 项目源码目录
├── js # js文件目录
│ ├── count.js
│ └── sum.js
└── main.js # 项目主文件2. 创建文件
// count.js
export default function count(x, y) {
return x - y
}
// sum.js
export default function sum(...args) {
return args.reduce((p, c) => p + c, 0)
}
// main.js
import count from './js/count'
import sum from './js/sum'
console.log(count(2, 1))
console.log(sum(1, 2, 3, 4))3. 下载依赖
打开终端,来到项目根目录。运行以下指令:
# 初始化`package.json`
pnpm init此时会生成一个基础的 package.json 文件。
需要注意的是 package.json 中 name 字段不能叫做 webpack, 否则下一步会报错
# 下载依赖
pnpm i webpack webpack-cli -D4. 启用 Webpack
# 开发模式
npx webpack ./src/main.js --mode=development
# 生产模式
npx webpack ./src/main.js --mode=productionnpx webpack: 是用来运行本地安装 Webpack 包的。
./src/main.js: 指定 Webpack 从 main.js 文件开始打包,不但会打包 main.js,还会将其依赖也一起打包进来。
--mode=xxx:指定模式(环境)。
使用 watch mode(观察模式)
在每次编译代码时,手动运行 npx webpack 会显得很麻烦。
我们可以在 webpack 启动时添加 "watch" 参数。如果其中一个文件被更新,代码将被重新编译,所以你不必再去手动运行整个构建。
npx webpack --watch类似于 nodemon,保存将会重新打包一次
现在,保存文件并检查 terminal(终端) 窗口。应该可以看到 webpack 自动地重新编 译修改后的模块!
唯一的缺点是,为了看到修改后的实际效果,你需要刷新浏览器。如果能够自动刷新浏览器就更好了,因此接下来我们会尝试通过 webpack-dev-server 实现此功能。
5. 观察输出文件
默认 Webpack 会将文件打包输出到 dist 目录下,我们查看 dist 目录下文件情况就好了
小结
Webpack 本身功能比较少,只能处理 js 资源,一旦遇到 css 等其他资源就会报错。
所以我们学习 Webpack,就是主要学习如何处理其他资源。
基本配置
在开始使用 Webpack 之前,我们需要对 Webpack 的配置有一定的认识。
webpack 打包的大致流程
- 以入口 Entry 为起点,分析构建内部依赖图,找出有哪些模块和库是 Entry 直接和间接依赖的;
- 开始处理找到的每个依赖项:
- webpack 可以直接处理 js, json 文件;
- webpack 处理不了的文件(css, less, scss, png, jpg...)使用 loader 处理;
- 使用 plugins 做优化、压缩等处理;
- 处理完后输出到文件中,这些文件我们称为 bundles;
- 处理完后获得了 bundles,webpack 会根据出口 output 的配置找到目标文件夹,创建目标文件,再把这些 bundles 写入目标文件。
5 大核心概念
- entry(入口):指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。默认值是
./src/index.js。 - output(输出):告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是
./dist/main.js,其他生成文件默认放置在./dist文件夹中。 - loader(加载器):webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析,可以将所有类型的文件转换为 webpack 能够处理的有效模块。
- plugins(插件):扩展 Webpack 的功能
- mode(模式)
主要由两种模式:
- 开发模式:development
- 生产模式:production
| 选项 | 描述 | 特点 |
|---|---|---|
| development | 会将DefinePlugin中process.env.NODE_ENV的值设置为development。启用NamedChunksPlugin和NamedModulesPlugin。 | 能让代码本地调试运行的环境 |
| production | 会将DefinePlugin中process.env.NODE_ENV的值设置为production。启用FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。 | 能让代码优化上线运行的环境 |
Webpack 配置文件
在项目根目录下新建文件:webpack.config.js
Webpack 是基于 Node.js 运行的,所以采用 COMMONJS 模块化规范
// Node.js的核心模块,专门用来处理文件路径
const path = require('path')
module.exports = {
// 入口
// 相对路径和绝对路径都行
entry: './src/main.js',
// 输出
output: {
// path: 文件输出目录,必须定义为绝对路径
// path.resolve()方法返回一个绝对路径
// __dirname 当前文件的文件夹绝对路径
path: path.resolve(__dirname, 'dist'),
// filename: 输出文件名
filename: 'main.js'
},
// 加载器
module: {
rules: []
},
// 插件
plugins: [],
// 模式
mode: 'development' // 开发模式
}项目通过 npx webpack 打包好了,我们得去引用打包好的 JS 文件。
处理样式资源
介绍
Webpack 本身是不能识别样式资源的,所以我们需要借助 Loader 来帮助 Webpack 解析样式资源
我们找 Loader 都应该去官方文档中找到对应的 Loader,然后使用
官方文档找不到的话,可以从社区 Github 中搜索查询
处理 CSS 资源
1. 下载包
pnpm i css-loader style-loader -D注意:需要下载两个 loader
2. 功能介绍
css-loader:负责将 CSS 文件编译成 Webpack 能识别的模块style-loader:会动态创建一个 Style 标签,里面放置 Webpack 中 CSS 模块内容
此时样式就会以 Style 标签的形式在页面上生效
3. 配置
// webpack.config.js
// 此处省略其他配置,只展示与当前内容相关的配置,下同
module.exports = {
module: {
rules: [
{
/**
* test 属性,识别出哪些文件会被转换
* use 属性,定义出在进行转换时,应该使用哪些 Loader
*/
// 用来匹配 .css 结尾的文件
test: /\.css$/, //
// use 数组里面 Loader 执行顺序是从右到左
use: ['style-loader', 'css-loader']
}
]
}
}4. 添加 CSS 资源
新增 CSS 文件,并在 main.js 中引入,这样就可以在 index.html 中使用样式,打包后可查看效果
.box1 {
width: 100px;
height: 100px;
background-color: pink;
}// 引入 CSS 资源,Webpack才会对其打包
import './css/index.css'<!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>处理 less 资源
1. 下载包
pnpm i less-loader -D2. 功能介绍
less-loader:负责将 Less 文件编译成 CSS 文件
3. 配置
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]4. 添加 Less 资源
新增 Less 文件,并在 main.js 中引入,这样就可以在 index.html 中使用样式,打包后可查看效果
.box2 {
width: 100px;
height: 100px;
background-color: deeppink;
}import './less/index.less'<div class="box2"></div>处理 sass 和 scss 资源
1. 下载包
pnpm i sass-loader sass -D2. 功能介绍
sass-loader:负责将 Sass 文件编译成 css 文件sass:sass-loader依赖sass进行编译
3. 配置
rules: [
{
test: /\.s[ac]ss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]4. 添加 sass 资源
新增 sass/scss 文件,并在 main.js 中引入,这样就可以在 index.html 中使用样式,打包后可查看效果
/* 可以省略大括号和分号 */
.box3
width: 100px
height: 100px
background-color: hotpink.box4 {
width: 100px;
height: 100px;
background-color: lightpink;
}import './sass/index.sass'
import './sass/index.scss'<div class="box3"></div>
<div class="box4"></div>此时如果查看 dist 目录的话,会发现样式资源没有单独的文件,因为经过 style-loader 的处理,样式资源打包到 main.js 里面去了,所以没有额外输出出来
抽离 CSS
CSS 文件目前被打包到 js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式
这样对于网站来说,会出现闪屏现象,用户体验不好
我们应该是单独的 CSS 文件,通过 link 标签加载性能才好
1. 下载包
pnpm i mini-css-extract-plugin -D2. 配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
},
{
test: /\.s[ac]ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
}
]
},
plugins: [
// 提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: 'static/css/main.css'
})
]
}CSS 兼容性处理
1. 下载包
pnpm i postcss-loader postcss postcss-preset-env -D2. 配置
// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {
return [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env' // 能解决大多数样式兼容性问题
]
}
}
},
preProcessor
].filter(Boolean)
}
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: getStyleLoaders()
},
{
test: /\.less$/,
use: getStyleLoaders('less-loader')
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders('sass-loader')
}
]
}
}3. 控制兼容性
配置 autoprefixer,插件 autoprefixer 提供自动给样式加前缀去兼容浏览器,
安装
pnpm i autoprefixer -D然后再在项目根目录下创建一个文件:postcss.config.js,这个文件是专门用来配置 css 的插件的。
module.exports = () => {
return {
plugins: {
autoprefixer: {} // 为CSS 规则添加前缀
}
}
}我们可以在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度。
{
// 其他省略
"browserslist": ["ie >= 8"]
}想要知道更多的 browserslist 配置,查看browserslist 文档
以上为了测试兼容性所以设置兼容浏览器 ie8 以上。
实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:
"browserslist": [
"> 1%",
"last 2 versions"
],last 2 versions: 每个浏览器中最新的两个版本。1% or >= 1%: 全球浏览器使用率大于 1%或大于等于 1%。
尽量不要用 browserslist 最好放在.browserslistrc 或者 package.json 内 postcss.config.js 内 overrideBrowserslist 的优先级高于 package.json 的 browserslist,且不能为空,否则会不生效。
CSS 压缩
1. 下载包
pnpm i css-minimizer-webpack-plugin -D2. 配置
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
plugins: [
// css压缩
new CssMinimizerPlugin()
]
}处理图片资源
过去在 Webpack4 时,我们处理图片资源通过 file-loader 和 url-loader 进行处理
现在 Webpack5 已经将两个 Loader 功能内置到 Webpack 里了,我们只需要简单配置即可处理图片资源
1. 配置
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/,
type: 'asset'
}
]type 值:
- asset/resource 发送一个单独的文件并导出 URL。
- asset/in1ine 导出一个资源的 data URl。
- asset/source 导出资源的源代码。
- asset 在导出一个 data URl 和发送一个单独的文件之间自动选择。
2. 添加图片资源
- src/images/1.jpeg
- src/images/2.png
- src/images/3.gif
3. 使用图片资源
.box1 {
width: 100px;
height: 100px;
background-image url("../images/3.gif");
background-size cover;
}// src/less/index.less
.box2 {
width: 100px;
height: 100px;
background-image: url('../images/1.jpeg');
background-size: cover;
}<!-- src/sass/index.sass -->
.box3
width: 100px
height: 100px
background-image: url("../images/2.png")
background-size: cover4. 输出资源情况
此时如果查看 dist 目录的话,会发现多了三张图片资源
因为 Webpack 会将所有打包好的资源输出到 dist 目录下
5. 对图片资源进行优化
将小于某个大小的图片转化成 data URI 形式(Base64 格式)
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
}
}
}
]- 优点:减少请求数量
- 缺点:体积变得更大
此时输出的图片文件就只有两张,有一张图片以 data URI 形式内置到 js 中了 (注意:需要将上次打包生成的文件清空,再重新打包才有效果)
修改输出资源的名称和路径
1. 配置
module.exports = {
output: {
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
},
module: {
rules: [
{
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参数
// generator 中自定义的输出文件名优先级大于 output 中的 assetModuleFilename
filename: "static/imgs/[hash:8][ext][query]",
},
},
],
},
};2. 修改 index.html
<!-- 修改 js 资源路径 -->
<script src="../dist/static/js/main.js"></script>- 此时输出文件目录:(注意:需要将上次打包生成的文件清空,再重新打包才有效果)
├── dist
└── static
├── imgs
│ └── 7003350e.png
└── js
└── main.js自动清空上次打包资源
配置
output: {
clean: true, // 自动将上次打包目录资源清空
}处理字体图标资源
1. 下载字体图标文件
选择想要的图标添加到购物车,统一下载到本地
2. 添加字体图标资源
src/fonts/iconfont.ttf
src/fonts/iconfont.woff
src/fonts/iconfont.woff2
src/css/iconfont.css
- 注意字体文件路径需要修改
在 main.js 中引入 iconfont.css
import './css/iconfont.css'<!-- 使用字体图标 -->
<i class="iconfont icon-arrow-down"></i>
<i class="iconfont icon-ashbin"></i>
<i class="iconfont icon-browse"></i>3. 配置
rules: [
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'static/media/[hash:8][ext][query]'
}
}
]type: "asset/resource"和type: "asset"的区别:
type: "asset/resource"相当于file-loader, 将文件转化成 Webpack 能识别的资源,其他不做处理type: "asset"相当于url-loader, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式
处理其他资源
开发中可能还存在一些其他资源,如音视频等,我们也一起处理了
配置
rules: [
{
test: /\.(ttf|woff2?|map4|map3|avi)$/,
type: 'asset/resource',
generator: {
filename: 'static/media/[hash:8][ext][query]'
}
}
]就是在处理字体图标资源基础上增加其他文件类型,统一处理即可
处理 js 资源
有人可能会问,js 资源 Webpack 不能已经处理了吗,为什么我们还要处理呢?
原因是 Webpack 对 js 处理是有限的,只能编译 js 中 ES 模块化语法,不能编译其他语法,导致 js 不能在 IE 等浏览器运行,所以我们希望做一些兼容性处理。
其次开发中,团队对代码格式是有严格要求的,我们不能由肉眼去检测代码格式,需要使用专业的工具来检测。
- 针对 js 兼容性处理,我们使用 Babel 来完成
- 针对代码格式,我们使用 Eslint 来完成
我们先完成 Eslint,检测代码格式无误后,在由 Babel 做代码兼容性处理
Eslint
可组装的 JavaScript 和 JSX 检查工具。
这句话意思就是:它是用来检测 js 和 jsx 语法的工具,可以配置各项功能
我们使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,将来运行 Eslint 时就会以写的规则对代码进行检查
1. 配置文件
配置文件由很多种写法:
eslint.config.*:新建文件,位于项目根目录(eslint 版本大于 8.21.0)package.json中eslintConfig:不需要创建文件,在原有文件基础上写
ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
2. 具体配置
3. 在 Webpack 中使用
- 下载包
pnpm i eslint-webpack-plugin eslint -D- 定义 Eslint 配置文件
在项目根目录下新建 eslint.config.js 文件
import { defineConfig } from 'eslint/config'
export default defineConfig([
{
files: ['./src/**/*.js'],
rules: {
'no-unused-vars': 'error'
}
}
])- 修改 js 文件代码
var a = 1- 配置 webpack
const ESLintWebpackPlugin = require('eslint-webpack-plugin')
module.exports = {
plugins: [new ESLintWebpackPlugin({})]
}Babel
JavaScript 编译器。
主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中
1. 配置文件
配置文件由很多种写法:
babel.config.*:新建文件,位于项目根目录babel.config.jsbabel.config.json
.babelrc.*:新建文件,位于项目根目录.babelrc.babelrc.js.babelrc.json
package.json中babel:不需要创建文件,在原有文件基础上写
Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可
2. 具体配置
我们以 babel.config.js 配置文件为例:
module.exports = {
// 预设
presets: []
}presets 预设,简单理解:就是一组 Babel 插件, 扩展 Babel 功能
@babel/preset-env: 一个智能预设,允许您使用最新的 JavaScript。@babel/preset-react:一个用来编译 React jsx 语法的预设@babel/preset-typescript:一个用来编译 TypeScript 语法的预设
3. 在 Webpack 中使用
- 下载包
pnpm i babel-loader @babel/core @babel/preset-env -D- 定义 Babel 配置文件,在项目根目录下新建 babel.config.js 文件
module.exports = {
presets: ['@babel/preset-env']
}- 修改 main.js 文件代码
import count from './js/count'
import sum from './js/sum'
const result1 = count(2, 1)
console.log(result1)
const result2 = sum(1, 2, 3, 4)
console.log(result2)- 配置
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: 'babel-loader'
}
]打开打包后的 dist/static/js/main.js 文件查看,会发现箭头函数等 ES6 语法已经转换了
JS 压缩
安装插件 terser-webpack-plugin
pnpm i terser-webpack-plugin -D配置
const TerserWebpackPlugin = require("terser-webpack-plugin")
module.exports = {
...
optimization: {
minimizer: [
// 使用插件压缩 js 代码 (生产模式)
new TerserWebpackPlugin({
parallel: true, //多线程压缩
extractComments: false //不要注释-因为默认会对每个压缩的文件生成一个txt的注释文本。没必要
})
]
}
}这个插件,当 mode 是 production 的时候,执行这个配置打包成 js 压缩的形式,而开发环境时,则是打包成原始的 js。
处理 Html 资源
1. 下载包
pnpm i html-webpack-plugin -D2. 配置
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
plugins: [
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, 'public/index.html')
filename: 'index.html', // 打包生成的文件名称。默认为index.html
// 可以用于html模板的title标签内容
title: 'dselegent',
// 设置所有资源文件注入模板的位置。可以设置的值
// true|'head'|'body'|false,默认值为 true
inject: 'body',
压缩html代码 production环境使用
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComment: true
}
})
]
}3. 修改 index.html
html 中去掉引入的 js 文件,因为 HtmlWebpackPlugin 会自动引入
<title><%= htmlWebpackPlugin.options.title %></title>此时 dist 目录就会输出一个 index.html 文件
默认生产模式已经开启了:html 压缩和 js 压缩。不需要额外进行配置
开发服务器&自动化
每次写完代码都需要手动输入指令才能编译代码,太麻烦了,我们希望一切自动化
1. 下载包
pnpm i webpack-dev-server -D2. 配置
module.exports = {
// 开发服务器
devServer: {
static: path.resolve(__dirname, './dist'), // 默认是把/dist目录当作web服务的根目录
host: 'localhost', // 启动服务器域名
port: '3000', // 启动服务器端口号
open: true, // 是否自动打开浏览器
compress: true, //可选择开启gzips压缩功能,对应静态资源请求的响应头里的Content-Encoding: gzip
hot: true, // 启用热模块功能
liveReload: true // 启用热加载功能
}
}为了方便,我们配置一下工程的脚本命令,在 package.json 的 scripts 里。
{
//...
"scripts": {
//...
"dev": "webpack serve --mode development"
}
}注意!如果您需要指定配置文件的路径,请在命令的后面添加 --config [path], 比如:
webpack serve --mode development --config webpack.config.js注意运行指令发生了变化
并且当你使用开发服务器时,所有代码都会在内存中编译打包,并不会输出到 dist 目录下。
开发时我们只关心代码能运行,有效果即可,至于代码被编译成什么样子,我们并不需要知道。
3. 添加响应头
有些场景需求下,我们需要为所有响应添加 headers,来对资源的请求和响应打入 标志,以便做一些安全防范,或者方便发生异常后做请求的链路追踪。比如:
module.exports = {
//...
devServer: {
// 在所有响应中添加首部内容
headers: {
'X-Fast-Id': 'p3fdg42njghm34gi9ukj'
}
}
}这时,在浏览器中右键检查(或者使用 f12 快捷键),在 Network 一栏查看任意资源访 问,我们发现响应头里成功打入了一个 FastId。
4. 开启代理
我们打包出的 js bundle 里有时会含有一些对特定接口的网络请求(ajax/fetch). 要注意,此时客户端地址是在 http://localhost:3000/ 下,假设我们的接口来自 http://localhost:4001/ ,那么毫无疑问,此时控制台里会报错并提示你跨域。 如何解决这个问题? 在开发环境下,我们可以使用 devServer 自带的 proxy 功 能:
module.exports = {
devServer: {
proxy: {
'/api': 'http://localhost:4001'
}
}
}现在,对 /api/users 的请求会将请求代理到 http://localhost:4001/api/users 。 如果不希望传递/api,则需要重写路径:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:4001',
pathRewrite: { '^/api': '' }
}
}
}
}默认情况下,将不接受在 HTTPS 上运行且证书无效的后端服务器。 如果需要,可以这样修改配置:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://other-server.example.com',
secure: false
}
}
}
}5. http
如果想让我们的本地 http 服务变成 https 服务,我们只需要这样配置:
module.exports = {
devServer: {
https: true // https//localhost...
}
}注意,此时我们访问 http://localhost:port 是无法访问我们的服务的,我们需要在地址栏里加前缀:https: 注意:由于默认配置使用的是自签名证书,所以有得浏览器会告诉你是不安全的,但我们依然可以继续访问它。当然我们也可以提供自己的证书——如果有的话:
module.exports = {
devServer: {
https: {
cacert: './server.pem',
pfx: './server.pfx',
key: './server.key',
cert: './server.crt',
passphrase: 'webpack-dev-server',
requestCert: true
}
}
}6. http2
如果想要配置 http2,那么直接设置:
devServer: {
http2: true, // https//localhost...
},即可,http2 默认自带 https 自签名证书,当然我们仍然可以通过 https 配置项来使用 自己的证书。
7. historyApiFallback
如果我们的应用是个 SPA(单页面应用),当路由到/some 时(可以直接在地址栏里输入),会发现此时刷新页面后,控制台会报错。
GET http://localhost:3000/some 404 (Not Found)此时打开 network,刷新并查看,就会发现问题所在———浏览器把这个路由当作了 静态资源地址去请求,然而我们并没有打包出/some 这样的资源,所以这个访问无疑是 404 的。 如何解决它? 这种时候,我们可以通过配置来提供页面代替任何 404 的静 态资源响应:
module.exports = {
//...
devServer: {
// 任意的 404 响应都被替代为 index.html
// 基于node connect-history-api-fallback包实现,主要应用于history路由刷新页面404的场景
historyApiFallback: true
}
}此时重启服务刷新后发现请求变成了 index.html。 当然,在多数业务场景下,我们需要根据不同的访问路径定制替代的页面,这种情况下,我们可以使用 rewrites 这个配置项。 类似这样:
module.exports = {
//...
devServer: {
historyApiFallback: {
rewrites: [
{ from: /^\/$/, to: '/views/landing.html' },
{ from: /^\/subpage/, to: '/views/subpage.html' },
{ from: /./, to: '/views/404.html' }
]
}
}
}8. 开发服务器主机
如果你在开发环境中起了一个 devserve 服务,并期望你的同事能访问到它,你只 需要配置:
module.exports = {
devServer: {
host: '0.0.0.0'
}
}这时候,如果你的同事跟你处在同一局域网下,就可以通过局域网 ip 来访问你的服务啦。
启动服务的时候有个 ipv4 的网络地址,可以通过那个访问
9. progress
在浏览器中以百分比显示编译进度。
module.exports = {
devServer: {
client: {
progress: true
}
}
}10. overlay
当出现编译错误或警告时,在浏览器中显示全屏覆盖。
module.exports = {
//...
devServer: {
client: {
overlay: true
}
}
}如果你只想显示错误信息:
module.exports = {
//...
devServer: {
client: {
overlay: {
errors: true,
warnings: false
}
}
}
}总结
本章节我们学会了 Webpack 基本使用,掌握了以下功能:
- 两种开发模式
- 开发模式:代码能编译自动化运行
- 生产模式:代码编译优化输出
- Webpack 基本功能
- 开发模式:可以编译 ES Module 语法
- 生产模式:可以编译 ES Module 语法,压缩 js 代码
- Webpack 配置文件
- 5 个核心概念
- entry
- output
- loader
- plugins
- mode
- devServer 配置
- Webpack 脚本指令用法
webpack直接打包输出webpack serve启动开发服务器,内存编译打包没有输出