主题
Webpack 配置
参考于:Webpack 配置
mode 模式
webpack 主要分为两种配置:development
和 production
。
在 webpack 配置文件中指定不同的模式,来告知 webpack 使用相应模式的内置优化。
DevTool
DevTool 用于控制是否生成,以及如何生成 source map。
对于 开发环境,一般选取如下几种:
eval
:每个模块都使用 eval() 执行,并且都有 //# sourceURL。此选项会非常快地构建。主要缺点是,由于会映射到转换后的代码,而不是映射到原始代码(没有从 loader 中获取 source map),所以不能正确的显示行数。eval-source-map
:每个模块使用 eval() 执行,并且 source map 转换为 DataUrl 后添加到 eval() 中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map。eval-cheap-source-map
:类似 eval-source-map,每个模块使用 eval() 执行。这是 "cheap(低开销)" 的 source map,因为它没有生成列映射(column mapping),只是映射行数。它会忽略源自 loader 的 source map,并且仅显示转译后的代码,就像 eval devtool。eval-cheap-module-source-map
:类似 eval-cheap-source-map,并且,在这种情况下,源自 loader 的 source map 会得到更好的处理结果。然而,loader source map 会被简化为每行一个映射(mapping)。
对于 生产环境,一般选取如下几种:
(none)(省略 devtool 选项)
:不生成 source map。这是一个不错的选择。source-map
:整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。hidden-source-map
:与 source-map 相同,但不会为 bundle 添加引用注释。如果你只想 source map 映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露你的 source map,这个选项会很有用。nosources-source-map
:创建的 source map 不包含 sourcesContent(源代码内容)。它可以用来映射客户端上的堆栈跟踪,而无须暴露所有的源代码。你可以将 source map 文件部署到 web 服务器。
context 上下文
context 指的是基础目录,它是一个 绝对路径,用于从配置中解析 入口点(entry point) 和 加载器(loader)。
entry 入口
一个需要考虑的规则:每个 HTML 页面都有一个入口起点。
单页应用(SPA):一个入口起点。
jsmodule.exports = { context: resolve(__dirname, '../'), entry: 'src/main.js', }
多页应用(MPA):多个入口起点。
jsmodule.exports = { context: resolve(__dirname), entry: { pc: './src/pages/pc/main.js', mobile: './src/pages/mobile/main.js', }, }
关于 chunk 名称
- 如果传入一个 字符串 或 字符串数组,chunk 会被命名为 main。
需要注意的是,如果是 字符串数组,会被合并成一个文件。 - 如果传入一个对象,则每个属性的键(key)会是 chunk 的名称,该属性的值描述了 chunk 的入口点。
output 输出
output 选项指示 webpack 如何去输出、以及在哪里输出你的「bundle、asset 和其他你所打包或使用 webpack 载入的任何内容」。
publicPath
对于按需加载(on-demand-load)或加载外部资源(external resources)(如图片、文件等)来说,output.publicPath
是很重要的选项。如果指定了一个错误的值,则在加载这些资源时会收到 404 错误。
js
module.exports = {
//...
output: {
// One of the below
// 它会自动从“import.meta.url”、“document.currentScript”、“<script />”或“self.location”确定公共路径。meta.url`, `document.currentScript`, `<script />` or `self.location`.
publicPath: 'auto',
// CDN(总是 HTTPS 协议)
publicPath: 'https://cdn.example.com/assets/',
// CDN(协议相同)
publicPath: '//cdn.example.com/assets/',
// 相对于服务(server-relative)
publicPath: '/assets/',
// 相对于 HTML 页面
publicPath: 'assets/',
// 相对于 HTML 页面
publicPath: '../assets/',
// 相对于 HTML 页面(目录相同)
publicPath: '',
},
}
hashFunction
散列算法,webpack 默认使用的是 md4
,一般我们可以使用更快速的 xxhash64
算法,来减少 CPU 消耗,并提升打包速度。
path
绝对路径,output 输出的目录。
filename
filename 决定了每个输出 bundle 的名称。这些 bundle 将写入到 output.path
选项指定的目录下。
chunkFilename
chunkFilename 选项决定了非初始(non-initial)chunk 文件的名称。
assetModuleFilename
assetModuleFilename
与 output.filename
相同,不过应用于 Asset Modules。
clean
js
module.exports = {
//...
output: {
// One of the below
// 在生成文件之前清空 output 目录
clean: true,
// 打印而不是删除应该移除的静态资源
clean: {
dry: true,
},
// 保留 'ignored/dir' 下的静态资源(写法1)
clean: {
keep: /ignored\/dir\//,
},
// 保留 'ignored/dir' 下的静态资源(写法2)
clean: {
keep(asset) {
return asset.includes('ignored/dir')
},
},
},
}
resolve 解析
resolve 选项能设置 Module 模块 如何被解析。
alias
alias 选项用于创建 import 或 require 的别名,来确保模块引入变得更简单。
js
module.exports = {
resolve: {
alias: {
'@': resolve(__dirname, '../src'),
},
},
}
module 模块
module 选项决定了如何处理项目中的不同类型的模块。
noParse
noParse 选项可以阻止 webpack 对特定的模块进行解析和处理。常用于那些不依赖其他模块,或者我们确定不需要处理其依赖关系的库,如:jQuery、lodash。
rules
创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式。 这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。
Rule
每个 Rule 规则可以分为三部分:
- 条件(condition)
- 结果(result)
- 嵌套规则(nested rule)
js
module.exports = {
module: {
rules: [
/* Rule */
{
test: /\.css$/, // 条件
use: 'css-loader', // 结果
rules: [
// 嵌套规则
{
resourceQuery: /inline/, // 条件
use: 'url-loader', // 结果
},
],
},
],
},
}
Rule.resource
Rule.resource
选项用于匹配模块的资源路径。它可以是一个函数,一个正则表达式,一个字符串,或者这些条件的数组。
js
module.exports = {
module: {
rules: [
{
resource: /my-directory/, // 只有在 'my-directory' 目录下的模块会被匹配
use: 'babel-loader',
},
],
},
}
Rule.resourceQuery
Rule.resourceQuery
选项用于匹配模块的查询字符串。它也可以是一个函数,一个正则表达式,一个字符串,或者这些条件的数组。
js
module.exports = {
module: {
rules: [
{
resourceQuery: /raw/, // 只有查询字符串包含 'raw' 的模块会被匹配
use: 'raw-loader',
},
],
},
}
Rule.test
Rule.test
选项用于测试模块的路径是否符合某个条件,通常是一个正则表达式。例如,你可能会使用 Rule.test
来匹配所有 .js
或 .css
文件,然后对这些文件应用特定的 loader。
注意:如果你提供了一个 Rule.test
选项,就不能再提供 Rule.resource
。
js
module.exports = {
module: {
rules: [
{
test: /\.css$/, // 使用 Rule.test 匹配所有 .css 文件
use: 'css-loader',
},
],
},
}
Rule.exclude
Rule.exclude
选项用于 排除 所有符合条件的模块。
如果你提供了 Rule.exclude
选项,就不能再提供 Rule.resource
。
Rule.include
Rule.include
选项用于 引入 所有符合条件的模块。
如果你提供了 Rule.include
选项,就不能再提供 Rule.resource
。
Rule.type
Rule.type
用于设置匹配模块的类型。可设置的值如下:
javascript/auto
:这是默认类型,允许在模块中混合使用 CommonJS 和 ESM。javascript/dynamic
:这个类型用于处理动态导入的模块。javascript/esm
:这个类型用于处理 ECMAScript 模块(ESM)。在 Webpack5 中,你可以使用 ESM 语法,并且 Webpack 会自动处理模块的导入和导出。json
:这个类型用于处理 JSON 文件。Webpack 会将 JSON 文件转换为 JavaScript 对象。webassembly/sync
:这个类型用于处理同步导入的 WebAssembly 模块。webassembly/async
:这个类型用于处理异步导入的 WebAssembly 模块。asset
:这个类型用于处理各种类型的资源文件,如图片、字体等。在 webpack 5 之前通过使用file-loader
实现。asset/source
:这个类型用于处理资源文件,Webpack 会将资源文件的源代码内联到 JavaScript bundle 中。在 webpack 5 之前通过使用url-loader
实现。asset/resource
:这个类型用于处理资源文件,Webpack 会将资源文件输出到构建目录,并在 JavaScript bundle 中包含对这些文件的 URL 引用。在 webpack 5 之前通过使用raw-loader
实现。asset/inline
:这个类型用于处理资源文件,Webpack 会将资源文件的内容作为 DataURL 内联到 JavaScript bundle 中。在 webpack 5 之前通过使用url-loader
,并且配置资源体积限制实现。
Rule.generator
Rule.generator.filename
与 output.assetModuleFilename
相同,并且仅适用于 asset
和 asset/resource
模块类型。
Rule.use
Rule.use
选项用于指定一个或多个 loader,这些 loader 将按照 从右到左 的顺序应用于匹配的模块。
在这个例子中,所有 .css
文件都会首先被 css-loader
处理,然后再被 style-loader
处理。
js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'], // 使用 Rule.use 指定多个 loader
},
],
},
}
Rule.oneOf
Rule.oneOf
选项用于指定一组规则,其中只有第一个匹配的规则会被应用。这意味着,如果你在 oneOf
中使用 Rule.use
,那么只有第一个匹配的 loader 会被应用,其他的 loader 将被忽略。
在这个例子中,对于每个模块,webpack 会首先检查它是否匹配 .css
文件。如果匹配,那么 css-loader
将被应用,然后跳过其他的规则。如果不匹配,那么 webpack 会继续检查它是否匹配 .less
文件。如果匹配,那么 less-loader
将被应用。
js
module.exports = {
module: {
rules: [
{
oneOf: [
{
test: /\.css$/,
use: 'css-loader', // 使用 Rule.use 指定 loader
},
{
test: /\.less$/,
use: 'less-loader', // 使用 Rule.use 指定 loader
},
],
},
],
},
}
Rule.loader
Rule.loader
是 Rule.use: [ { loader } ]
的简写。这意味着你可以使用 Rule.loader
来更简洁地指定 loader。
这是 Rule.loader
的例子:
js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
loader: 'css-loader', // 使用 Rule.loader 指定 loader
},
],
},
}
这是相同配置的 Rule.use: [ { loader } ]
的写法:
js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'css-loader', // 使用 Rule.use: [ { loader } ] 指定 loader
},
],
},
],
},
}
在这两个例子中,所有 .css
文件都会被 css-loader
处理。你可以看到,Rule.loader
提供了一种更简洁的方式来指定 loader,而 Rule.use: [ { loader } ]
则提供了更多的灵活性,允许你为 loader 指定选项。
Rule.options / Rule.query
Rule.options
和 Rule.query
是 Rule.use: [ { options } ]
的简写。
注意:
Rule.query
已经被废弃,建议使用Rule.options
。
这是 Rule.options
的例子:
js
module: {
rules: [
{
test: /\.css$/,
loader: 'style-loader',
options: {
// 这里是你的选项
},
},
]
}
在上面的例子中,options
对象中的选项将被传递给 style-loader
。
这是相同配置的 Rule.use: [ { options } ]
的写法:
js
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
options: {
// 这里是你的选项
},
},
],
},
]
}
DevServer
http2 / https
默认情况下,开发服务器将通过 HTTP 提供服务。https2、https 需要手动开启。
headers
为所有响应添加 headers。
host
指定使用的 host。取值如下:
local-ip
:将 local-ip 指定为主机将尝试将主机选项解析为本地 IPv4 地址(如果可用),如果 IPv4 不可用,它将尝试解析本地 IPv6 地址。local-ipv4
:将 local-ipv4 指定为主机将尝试将主机选项解析为本地 IPv4 地址。local-ipv4
:指定 local-ipv6 作为主机将尝试将主机选项解析为您的本地 IPv6 地址。
如果想让你的服务器可以被外部访问,需如下指定:
js
module.exports = {
devServer: {
host: '0.0.0.0',
},
}
port
指定使用的 port 端口号。
port 配置项不能设置为 null 或者空字符串,要想自动使用一个可用端口请使用 port: 'auto'
。
hot vs liveReload
hot
是用于配置模块热替换(Hot Module Replacement)。 HMR 是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面 ¹。当你修改了源代码文件时,webpack-dev-server
会自动重新编译并刷新浏览器,使你能够即时看到修改后的效果 ⁵。liveReload
是一个在文件改变时自动刷新页面的功能。它与hot
的区别在于,hot
只替换发生改变的模块,而liveReload
会刷新整个页面。
为了让 liveReload 能够生效,
devServer.hot
配置项必须禁用或者devServer.watchFiles
配置项必须启用。
proxy
当拥有单独的 API 后端开发服务器并且希望在同一域上发送 API 请求时,代理某些 URL 可能会很有用。
static
static 配置项允许配置从目录提供静态文件的选项(默认是 public
文件夹)。
Stats 对象
stats 选项让你更精确地控制 bundle 信息该怎么显示。 如果你不希望使用 quiet 或 noInfo 这样的不显示信息,而是又不想得到全部的信息,只是想要获取某部分 bundle 的信息,使用 stats 选项是比较好的折衷方式。
externals 外部扩展
防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
例如,从 CDN 引入 jQuery,而不是把它打包:
index.html
html<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
webpack.config.js
jsmodule.exports = { //... externals: { jquery: 'jQuery', }, }
使用
jsimport $ from 'jquery' $('.my-element').animate(/* ... */)
optimization 优化
从 webpack 4 开始,会根据你选择的 mode 来执行不同的优化, 不过所有的优化还是可以手动配置和重写。
splitChunks
将所有 js 文件全部引入一个 js 文件中,文件会变得很大。而且有些没有用到的模块也在一开始就加载了,使用 import 动态导入,实现 js 文件的按需加载。分成若干小的 js 文件,对于并行处理也更快。
js
splitChunks: {
chunks: 'all' // 对所有模块都进行分割
},
RuntimeChunk
在做了缓存优化后,浏览器从第二次开始,一般就会读取缓存文件,速度也会加快,为了确保缓存不失效,所以文件在打包时,一般需要做 hash 处理,确保更新后,每次不一样,浏览器就不会出现更新异常了。但是问题来了,每次更新都不一样,那缓存不久失效了吗?所以通过 runtime 文件单独存储 hash 值,当某些文件更新,别的文件不更新时,不需要全部更新,只需要更新 runtime 内各个文件的关系就好啦。
js
runtimeChunk: {
name: entrypoint => `runtime~${entrypoint.name}.js`
}