01 【前言 基础使用 核心概念】
01 【前言 基础使用 核心概念】
1.前言
1.1 概述
官网的描述:
本质上,webpack是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个静态资源(bundle)。
转换成自己的话:
- webpack 是前端的一个资源构建工具,一个静态模块打包器;
- 在 webpack 看来,前端的所有资源文件(js/json/css/less/scss/img...)都是一个个模块;
- webpack 会根据资源的依赖关系生成一个依赖关系图,再打包成对应的静态资源bundle。
1.2 为什么需要打包工具?
开发时,我们会使用框架(React、Vue),ES6 模块化语法,Less/Sass 等 css 预处理器等语法进行开发。
这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、Css 等语法,才能运行。
所以我们需要打包工具帮我们做完这些事。
除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。
1.3 为什么需要 Webpack
想要理解为什么要使用 webpack,我们先回顾下历史,在打包工具出现之前,我们 是如何在 web 中使用 JavaScript 的。在浏览器中运行 JavaScript 有两种方法:
第一种方式,引用一些脚本来存放每个功能,比如下面这个文档:
01-why-webpack/index-1.html
<!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 文件, 对上面的文档做改进:
01-why-webpack/index-2.html
<!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>
这种方式解决了方式一的问题,但会导致作用域、文件大小、可读性和可维护性方面 的问题。如何解决这些问题,请往下阅读。
1.4 如何解决作用域问题
早先前,我们使用 Grunt
和 Gulp
两个工具来管理我们项目的资源。
这两个工具称为任务执行器,它们将所有项目文件拼接在一起。利用了 立即调用函数 表达式(IIFE) - Immediately invoked function expressions
, 解决了大型项目 的作用域问题;当脚本文件被封装在 IIFE 内部时,你可以安全地拼接或安全地组合 所有文件,而不必担心作用域冲突。
什么是IIFE,参见下面的代码:
当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问。
function () { 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 中的某个函数,也必须在构建结果中加入整个库,然后将它们压缩在一起。大规模地实现延迟加载代码块及无用代码的去除,需要开发人员手动地 进行大量工作。
01-why-webpack/index-3.html
<!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>
1.5 如何解决代码拆分问题
感谢 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 项目的绝佳解决 方案,但浏览器不支持模块,我们似乎又遇到了新问题。
1.6 如何让浏览器支持模块
在早期,我们应用 Browserify
和RequireJS
等打包工具编写能够在浏览器中运行 的 CommonJS 模块:
目前,我们还有一个选择,就是来自 Web 项目的好消息是,模块正在成为 ECMAScript 标准的官方功能。然而,浏览器支持不完整,版本迭代速度也不够快, 还是推荐上面两个早期模块实现。早期的任务构建工具基于 Google 的 Closure 编译 器,要求我们手动在顶部声明所有的依赖,开发体验不好。
1.7 Webpack 搞定这一切
是否可以有一种方式,不仅可以让我们编写模块,而且还支持任何模块格式(至少在 我们到达 ESM 之前),并且可以同时处理resource
和 assets
?
这就是 webpack 存在的原因。它是一个工具,可以打包你的 JavaScript
应用程序 (支持 ESM 和 CommonJS),可以扩展为支持许多不同的静态资源,例如: images
, fonts
和 stylesheets
。
webpack
关心性能和加载时间;它始终在改进或添加新功能,例如:异步地加载和 预先加载代码文件,以便为你的项目和用户提供最佳体验。
2.基础使用
Webpack
是一个静态资源打包工具。
它会以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去。
输出的文件就是编译好的文件,就可以在浏览器段运行了。
我们将 Webpack
输出的文件叫做 bundle
。
2.1 功能介绍
Webpack 本身功能是有限的:
- 开发模式:仅能编译 JS 中的
ES Module
语法 - 生产模式:能编译 JS 中的
ES Module
语法,还能压缩 JS 代码
2.2 安装
首先使用 npm init 初始化项目,然后安装 webpack 以及 webpack-cli 。
// 全局安装
pnpm i webpack webpack-cli -g
// 本地安装(推荐)
pnpm i webpack webpack-cli -D
我的webpack版本:"webpack": "^5.44.0", "webpack-cli": "^4.7.2";
2.3 开始使用
2.3.1 资源目录
webpack_code # 项目根目录(所有指令必须在这个目录运行)
└── src # 项目源码目录
├── js # js文件目录
│ ├── count.js
│ └── sum.js
└── main.js # 项目主文件
2.3.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));
2.3.3 下载依赖
打开终端,来到项目根目录。运行以下指令:
- 初始化
package.json
npm init -y
此时会生成一个基础的 package.json
文件。
需要注意的是 package.json
中 name
字段不能叫做 webpack
, 否则下一步会报错
- 下载依赖
npm i webpack webpack-cli -D
2.3.4 启用 Webpack
- 开发模式
npx webpack ./src/main.js --mode=development
- 生产模式
npx webpack ./src/main.js --mode=production
npx webpack
: 是用来运行本地安装 Webpack
包的。
./src/main.js
: 指定 Webpack
从 main.js
文件开始打包,不但会打包 main.js
,还会将其依赖也一起打包进来。
--mode=xxx
:指定模式(环境)。
2.3.5 观察输出文件
默认 Webpack
会将文件打包输出到 dist
目录下,我们查看 dist
目录下文件情况就好了
[dselegent] 03-try-webpack $ npx webpack
asset main.js 50 bytes [emitted] [minimized] (name: main)
orphan modules 81 bytes [orphan] 1 module
./src/index.js + 1 modules 135 bytes [built] [code generated]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to
'production' for this value.
Set 'mode' option to 'development' or 'production' to enable
defaults for each environment.
You can also set it to 'none' to disable any default behavior.
Learn more: https://webpack.js.org/configuration/mode/
webpack 5.54.0 compiled with 1 warning in 197 ms
在这里,我们在没有任何配置的情况下运行 Webpack
(通常你会为 Webpack 提供 一个配置文件,现在,自Webpack4 开始,可以使用默认配置来打包文件了)。
这里还有一个警告:“mode” 选项尚未设置。我们将在本课程后面讨论“mode”选项。
从结果来看,webpack 为我们生成了一个main.js文件,具体见下图:
我们来看一下 main.js 里有什么:
03-try-webpack/dist/main.js
(()=>{"use strict";console.log("Hello world")})();
运行npx实际上就是执行npde_modules中已经下载好的依赖,这里是webpack. 打包的webpack默认是把src下的index.js文件作为入口文件,这样一来呢就会把相关的代码(相互依赖关系的代码)一起打包成一个js文件。 可以看到,它打包出来是个IIFE,里面还是箭头函数。
2.4 自定义 Webpack 配置
实际上, webpack-cli 给我们提供了丰富的终端命令行指令,可以通过webpack -- help
来查看。
可是命令行不方便也不直观,而且还不利于保存配置的内容。因此,webpack 还给我们提供了通过配置文件,来自定义配置参数的能力。
03-try-webpack/webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
// 输出文件夹必须定义为绝对路径
path: './dist'
},
mode: 'none'
}
我们发现,打包并没有成功,因为 webpack 要求我们打包配置 output.path
的路径必须为绝对路径,通过 path 模块来定义输出路径为绝对路径:
03-try-webpack/webpack.config.js
// 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", // 开发模式
//设置打包的模式:开发/测试/生产,这个和process.env.NODE_ENV息息相关,我们在package.json中用命令行设置:webpack --mode=development
};
项目文件通过 webpack 打包好了,我们得去引用打包好了的 JS才对。修改 index.html
03-try-webpack/index.html
<!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="./dist/bundle.js"></script>
</body>
</html>
这样打包出来的文件:
对于大多数项目,我们建议本地安装。这可以在引入重大更新(breaking change)版 本时,更容易分别升级项目。 通常会通过运行一个或多个 npm scripts 以在本地 node_modules 目录中查找安装的 webpack, 来运行 webpack:
"scripts": {
"build": "webpack --config webpack.config.js"
}
提示: 想要运行本地安装的 webpack,你可以通过 node_modules/.bin/webpack 来 访问它的二进制版本。另外,如果你使用的是 npm v5.2.0 或更高版本,则可以 运行 npx webpack 来执行。
2.5 清理dist
仔细留意一下,我们发现 dist/index.html
仍旧存在,这个文件是上次生成的残留 文件,已经没有用了。可见,webpack
将生成文件并放置在 /dist
文件夹中,但是 它不会追踪哪些文件是实际在项目中用到的。通常比较推荐的做法是,在每次构建前 清理 /dist
文件夹,这样只会生成用到的文件。让我们使用 output.clean 配置项 实现这个需求。
04-manage-output/webpack.config.js
module.exports = {
//...
output: {
//...
// 打包前清理 dist 文件夹
clean: true
},
//...
}
检查/dist
文件夹。现在只会看到构建后生成的文件,而没有旧文件!
2.6 source map
当 webpack
打包源代码时,可能会很难追踪到 error(错误) 和 warning(警告) 在源代 码中的原始位置。例如,如果将三个源文件( a.js , b.js 和 c.js
)打包到一个 bundle( bundle.js )
中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直 接指向到 bundle.js
。你可能需要准确地知道错误来自于哪个源文件,所以这种提 示这通常不会提供太多帮助。
开发模式下默认生成的sourcemap记录的是生成后代码的位置,不是源代码的位置。
为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 功能,可以 将编译后的代码映射回原始源代码。如果一个错误来自于 b.js ,source map 就会 明确的告诉你。
source map
是一种提供源代码到构建后代码映射的技术,如果构建后代码出错了,通过映射关系可以追踪源代码的错误。在 webpack.config.js
文件中配置
module.exports = {
// 在开发模式下追踪代码具体位置
devtool: 'inline-source-map',
};
在浏览器中打开生成的 index.html 文件,并且在控制台查看显示的错误。 错误如下:
在浏览器里点击 hellow-world.js:3
, 查看具体错误:
常用的几种 source-map 类型
eval:(默认配置)不会生成source map文件,每个模块都封装在eval中,并在后面添加
//# sourceURL = xx.js
当关闭devtool时: 当打开eval时:会将每个模块都包裹在一个eval中。source-map:生成外部文件,错误代码的准确信息和源代码的错误位置
额外生成一个source.map文件,打包文件bundle.js末尾会引入,可以映射源代码。
inline-source-map:内联,错误代码的准确信息和源代码的错误位置。在代码底部生成,构建速度比外部文件更快 不生成映射文件,而是生成一个dataURL形式的映射文件放置在bundle.js末尾。
hidden-source-map:生成外部文件,错误代码的原因,没有错误位置,无法追踪源代码错误。 和source-map一样,额外生成一个source.map文件,但打包文件bundle.js末尾没有引入,也就是不能映射源代码。
eval-source-map:内联,错误代码的准确信息和源代码的错误位置。每一个文件都生成对应的 source-map 每个module都会通过eval()来执行,并且生成一个DataUrl形式的url。和inline-source-map不同的是,inline-source-map只会生成一个dataurl放置在bundle.js的末尾。而eval()是每个module都生成。
cheap-source-map:生成外部文件,错误代码的准确信息和源代码的错误位置。只精确到行 生成一个没有列信息column-mapping(实际代码调试时,我们并不需要列信息)的sourceMaps文件,需要注意的是,一些需要loader编译的代码,如babel先编译,再打包的代码,这种配置是不会映射到的。
cheap-module-source-map:同 cheap-source-map,会将 loader 的 source map 加入 生成一个没有列信息(column-mapping)的sourceMaps文件,同时loader的sourcemap也会被简化为只包含对应行的。
开发环境和生产环境的要求:
1、开发环境:要求速度快,调试更友好
我们在开发环境时,最推荐采用这种:cheap-module-source-map
2、生产环境:(none)
(省略 devtool
选项) - 不生成 source map。这是一个不错的选择。
2.7 使用 watch mode(观察模式)
在每次编译代码时,手动运行 npx webpack 会显得很麻烦。
我们可以在 webpack 启动时添加 "watch" 参数。如果其中一个文件被更新,代码将被重新编译,所以你不必再去手动运行整个构建。
npx webpack --watch
类似于nodemon,保存将会重新打包一次
现在,保存文件并检查 terminal(终端) 窗口。应该可以看到 webpack 自动地重新编 译修改后的模块!
唯一的缺点是,为了看到修改后的实际效果,你需要刷新浏览器。如果能够自动刷新浏览器就更好了,因此接下来我们会尝试通过 webpack-dev-server
实现此功能。
3.核心概念
webpack打包的大致流程
- 以入口Entry为起点,分析构建内部依赖图,找出有哪些模块和库是Entry直接和间接依赖的;
- 开始处理找到的每个依赖项:
- webpack可以直接处理js, json文件;
- webpack处理不了的文件(css, less, scss, png, jpg...)使用loader处理;
- 使用plugins做优化、压缩等处理;
- 处理完后输出到文件中,这些文件我们称为bundles;
- 处理完后获得了bundles,webpack会根据出口output的配置找到目标文件夹,创建目标文件,再把这些bundles写入目标文件。
3.1 Entry 入口
入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
默认值是 ./src/index.js
,但你可以通过在 webpack configuration 中配置 entry
属性,来指定一个(或多个)不同的入口起点。例如:
module.exports = {
entry: './path/to/my/entry/file.js',
};
3.2 Output 输出
output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中。
你可以通过在配置中指定一个 output
字段,来配置这些处理过程:
// webpack.config.js:
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
// 输出路径:
// --dirname: nodeJs的变量,代表当前文件的绝对路径
// resolve: nodeJs里path模块的方法,用来拼接绝对路径
path: path.resolve(__dirname, 'dist'),
// 输出文件名
filename: 'my-first-webpack.bundle.js'
}
};
在上面的示例中,我们通过 output.filename
和 output.path
属性,来告诉 webpack bundle 的名称,以及我们想要 bundle 生成(emit)到哪里。可能你想要了解在代码最上面导入的 path 模块是什么,它是一个 Node.js 核心模块,用于操作文件路径。
3.3 Loader 解析器
- loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript 和 JSON 文件);
- loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块。
Warning
webpack 的其中一个强大的特性就是能通过
import
导入任何类型的模块(例如.css
文件),其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是很有必要的,因为这可以使开发人员创建出更准确的依赖关系图。
在更高层面,在 webpack 的配置中,loader 有两个属性:
test
属性,识别出哪些文件会被转换。use
属性,定义出在进行转换时,应该使用哪个 loader。
const path = require('path');
module.exports = {
output: {
filename: 'my-first-webpack.bundle.js',
},
module: {
// 不同类型的文件必须配置不同的规则来处理
rules: [{
// 匹配什么样的文件
test: /\.txt$/,
// use数组中loader的执行顺序:从下到上,从右到左(后进先出)
use: 'raw-loader' }],
},
};
以上配置中,对一个单独的 module 对象定义了 rules
属性,里面包含两个必须属性:test
和 use
。这告诉 webpack 编译器(compiler) 如下信息:
“嘿,webpack 编译器,当你碰到「在
require()
/import
语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先 use(使用)raw-loader
转换一下。
Warning
重要的是要记住,在 webpack 配置中定义 rules 时,要定义在
module.rules
而不是rules
中。为了使你便于理解,如果没有按照正确方式去做,webpack 会给出警告。
Warning
请记住,使用正则表达式匹配文件时,你不要为它添加引号。也就是说,
/\.txt$/
与'/\.txt$/'
或"/\.txt$/"
不一样。前者指示 webpack 匹配任何以 .txt 结尾的文件,后者指示 webpack 匹配具有绝对路径 '.txt' 的单个文件; 这可能不符合你的意图。
在使用 loader 时,可以阅读 loader 章节 查看更深入的自定义配置。
3.4 Plugins 插件
实际上webpack实现的功能并不全是主包的,可以说主包只是提供了一个平台。真正完成一个项目的资源打包编译的,还是众多的webpack插件:
官网地址:https://webpack.js.org/plugins/ 总共分为三类:
1,社区的插件
2,webpack内置的插件
3,第三方插件
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
Tip
查看 插件接口(plugin interface),学习如何使用它来扩展 webpack 能力。
想要使用一个插件,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new
操作符来创建一个插件实例。
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件
module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
在上面的示例中,html-webpack-plugin
为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。
Tip
webpack 提供许多开箱可用的插件!查阅 插件列表 获取更多。
在 webpack 配置中使用插件是简单直接的。然而,也有很多值得我们进一步探讨的用例。查看这里了解更多。
3.5 Mode 模式
模式(Mode)指示 webpack 使用相应模式的配置;
mode: 'development' | 'production' | 'none'
;生产环境和开发环境将ES6模块化编译成浏览其能识别的模块化;
生产环境比开发环境多一个压缩js代码;
生产环境比开发环境多一个压缩js代码;
选项 | 描述 | 特点 |
---|---|---|
development | 会将DefinePlugin 中p 的值设置为development 。启用NamedChunksPlugin 和NamedModulesPlugin 。 | 能让代码本地调试运行的环境 |
production | 会将DefinePlugin 中p 的值设置为production 。启用FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin 。 | 能让代码优化上线运行的环境 |
通过选择 development
, production
或 none
之中的一个,来设置 mode
参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production
。
// webpack.config.js:
module.exports = {
mode: 'production'
}
想要了解更多,请查阅 mode 配置,这里有具体每个值相应的优化行为。