Webpack 学习笔记
第一章:走进 Webpack
1. 是什么?
webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。

现代的 modern:因为现代前端开发面临各种各样的问题,才催生了 webpack 的出现和发展。
静态的 static:这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器)。
模块化 module:webpack 默认支持各种模块化开发,ES Module、CommonJS、AMD 等。
打包 bundler:webpack 可以将帮助我们进行打包,所以它是一个打包工具。
在 webpack 的世界中,一切皆是模块。
2. 安装
# 全局安装
npm install webpack webpack-cli -g
# 局部安装(推荐)
npm install webpack webpack-cli -D3. 快速入门
在项目根路径下直接执行 webpack 命令。
发现生成了一个 dist 文件夹,里面存放了一个 main.js 的文件,就是我们打包之后的文件。这个文件中的代码被压缩和丑化了。
另外我们还发现代码中依然存在 ES6 的语法,比如箭头函数、 const 等,这是因为默认情况下 webpack 并不清楚我们打包后的文件是否需要转成 ES5 之前的语法,后续我们需要通过 babel 来进行转换和设置。
但是有一个问题,webpack 是如何确定我们的入口的呢?
事实上,当我们运行 webpack 时, webpack 会查找当前目录下的 src 下的 index.js 作为入口。所以,如果当前项目中没有存在 src 下的 index.js 文件,那么会报错。当然,我们也可以通过配置来指定入口和出口。
npx webpack --entry ./src/main.js --output-path ./build --mode=development其实,--entry 选项可以省略不写。
第二章:Webpack 配置文件
一、5 大核心概念
1)entry(入口)
指示 Webpack 从哪个文件开始打包。
2)output(输出)
指示 Webpack 打包完的文件输出到哪里去,如何命名等。
3)loader(加载器)
webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析。
4)plugins(插件)
扩展 Webpack 的功能
5)mode(模式)
主要由两种模式: 开发模式:development 生产模式:production
二、准备配置文件
在通常情况下,webpack 需要打包的项目是非常复杂的,并且我们需要一系列的配置来满足要求,默认配置必然是不可以的。
因此,可以在根目录下创建一个 webpack.config.js 文件,来作为 webpack 的配置文件。Webpack 默认会查找项目根目录下的 webpack.config.js 文件作为配置文件。以下是一些常见的配置选项:
context: 设置 webpack 的基本目录,用于从配置中解析入口起点 (entry point) 和加载器 (loader)。entry: 指定 webpack 的入口点。这通常是你的应用程序的主文件,例如main.js。output: 指定 webpack 输出的文件名和路径。它是一个对象,包含filename和path属性。module: 用于配置加载器(loader)。加载器可以将所有类型的文件转换为 webpack 可以处理的模块。plugins: 用于配置插件。插件可以执行各种各样的任务,如清理构建目录、压缩代码、生成 HTML 文件等。resolve: 用于配置模块解析的方式,例如,可以设置文件扩展名的解析顺序,别名等。devServer: 配置 webpack-dev-server,一个提供了热模块替换等功能的开发服务器。mode: 设置构建模式,可以是development、production或none。不同的模式会启用不同的内置优化。optimization: 用于配置优化选项,例如代码拆分、压缩等。target: 设置编译环境,例如web、node等。devtool: 选择一种 source map 格式以增强调试过程。
基本的配置文件如下:
const path = require('path');
module.exports = {
entry: './src/index.js', // 入口文件
output: { // 输出
filename: 'bundle.js', // 输出文件名
// filename: "static/js/main.js", // 将 js 文件输出到 ./build/static/js 目录中
path: path.resolve(__dirname, './build') // 输出路径
clean: true, // 自动将上次打包目录资源清空
},
module: { // 模块,定义了对模块的处理逻辑
rules: [
{
test: /\.css$/, // 对所有 .css 文件使用 style-loader 和 css-loader
use: [
'style-loader',
'css-loader'
]
}
]
},
plugins: [ // 插件列表
// 使用插件
],
mode: 'development' // 'production' 用于生产模式
};如果配置文件不在项目的根目录,或者有多个配置文件,或者设置的名字不是 webpack.config.js,那么就可以使用 --config 选项来指定配置文件的位置与名字,例如:
webpack --config ./path/to/your/wk.config.js上面命令太长了,可以使用 package.json 中的 scripts 简化命令。用 npm run xxx 来代替上面的命令。一般都是 npm run build。

二、配置项解释
1. context
context 是 webpack 配置中的一个属性,它用于设置 webpack 的基本目录。这个基本目录是 webpack 用来解析配置中的入口起点(entry point)和加载器(loader)的路径的。
例如,如果项目结构如下:
/my-project
|-- /src
| |-- index.js
|-- /node_modules
|-- webpack.config.jswebpack.config.js 文件可能如下:
module.exports = {
context: __dirname, // 当前目录
entry: './src/index.js',
// ...
};context 被设置为 __dirname,这是一个 Node.js 全局变量,表示当前文件的目录。因此,entry 的路径 ./src/index.js 将从当前目录(即 webpack.config.js 所在的目录)开始解析。
如果你没有设置 context,那么 webpack 会默认使用当前工作目录(即你运行 webpack 命令的目录)作为基本目录。
2. 模式(Mode)
只需在配置对象(webpack.config.js)中提供 mode 选项:
module.exports = {
mode: 'development',
};支持以下字符串值:
| 选项 | 描述 |
|---|---|
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development. 为模块和 chunk 启用有效的名。 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin 和 TerserPlugin 。 |
none | 不使用任何默认优化选项。 |
如果没有设置,webpack 会给 mode 的默认值设置为 production。
webpack 给每种模式都预设了一些配置,所以在使用 webpack 时,最好都写上 Mode。


3. resolve
resolve 可以帮助 webpack 从每个 require/import 语句中,找到需要引入到合适的模块代码。webpack 使用 enhanced resolve (Node.js 模块) 来解析文件路径。
webpack 能解析三种文件路径
绝对路径:由于已经获得文件的绝对路径,因此不需要再做进一步解析。
相对路径:在这种情况下,使用 import 或 require 的资源文件所处的目录,被认为是上下文目录;在 import/require 中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径;
模块路径:在 resolve.modules 中指定的所有目录检索模块。默认值是 ['node_modules '],所以默认会从 node_modules 中查找文件。
确定文件还是文件夹
如果是一个文件:
- 如果文件具有扩展名,则直接打包文件。否则,将使用 resolve.extensions 选项作为文件扩展名解析。
如果是一个文件夹:
- 会在文件夹中根据 resolve.mainFiles 配置选项中指定的文件顺序查找。resolve.mainFiles 的默认值是 ['index']。再根据 resolve.extensions 来解析扩展名。
1)extensions
extensions:这个配置项是一个数组,用于指定 Webpack 在解析模块时应该自动添加哪些文件扩展名。例如,如果设置 extensions 为 ['.js', '.jsx'],那么在导入文件时,可以省略 .js 和 .jsx 扩展名。如下:
module.exports = {
// ...
resolve: {
extensions: ['.js', '.jsx']
}
};就可以像这样导入模块:
import MyComponent from './MyComponent'; // 实际上是 './MyComponent.jsx'2)mainFiles
resolve.mainFiles 是一个数组,它用于描述当模块的路径指向一个目录时,Webpack 应该尝试哪些文件名来解析模块。换句话说,当你尝试导入一个目录(而不是具体的文件)时,Webpack 会自动尝试这个目录下的哪些文件作为模块的入口。
例如:
module.exports = {
// ...
resolve: {
mainFiles: ['index', 'default']
}
};在这个例子中,如果你尝试导入一个目录,比如 import Foo from './Foo',Webpack 会首先尝试解析 ./Foo/index.js,如果这个文件不存在,它会尝试解析 ./Foo/default.js。
注意,resolve.mainFiles 的默认值是 ['index'],所以如果你没有提供这个配置项,Webpack会默认尝试解析 index.js。
3)alias 别名
在 webpack 中,可以使用 resolve.alias 选项来创建别名,来更方便地导入某些模块。例如,想要避免在代码中使用相对路径。
以下是一个配置别名的例子:
const path = require('path');
module.exports = {
// ...
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/'),
},
},
// ...
};在这个例子中,创建了一个别名 '@',它指向了 'src/' 目录。这意味着,在代码中使用 '@' 来替代 'src/'。例如,可以这样导入一个模块:
import MyComponent from '@/components/MyComponent';这将导入 'src/components/MyComponent.js' 文件(假设已经配置了文件扩展名的解析顺序)。
注意,path.resolve(__dirname, 'src/') 会生成一个绝对路径,这是必要的,因为 webpack 需要绝对路径来准确地解析文件。
4. 热更新
1)webpack-dev-server
webpack-dev-server 是一个用于开发环境的简单的 HTTP 服务器,它提供了实时重载(live reloading)功能。这意味着当修改项目文件时,webpack 将重新编译代码,然后 webpack-dev-server 将自动刷新浏览器显示新的结果。
首先,需要安装 webpack-dev-server。使用 npm 或 yarn 来安装:
npm install --save-dev webpack-dev-server
# or
yarn add webpack-dev-server --dev然后,在 webpack.config.js 文件中,添加一个 devServer 配置项(可选):
module.exports = {
// ...
devServer: {
host: "localhost", // 启动服务器域名
compress: true,
port: 9000,
open: true,
},
// ...
};在这个配置中:
compress启用 gzip 压缩。port指定了要监听请求的端口号。open是否打开浏览器。默认值是 false,设置为 true 会打开浏览器。也可以设置为类似于 Google Chrome 等值。
然后,在 package.json 文件的 scripts 部分添加一个脚本来启动 webpack-dev-server:
"scripts": {
"start": "webpack-dev-server --open",
// 或者下面的命令
// "serve": "webpack serve"
// ...
}现在,就可以运行 npm start(或 yarn start)来启动服务器,然后在浏览器中打开 http://localhost:9000/ 来查看项目。
注意运行指令发生了变化
并且当你使用开发服务器时,所有代码都会在内存中编译打包,并不会输出到 dist 目录下。
开发时我们只关心代码能运行,有效果即可,至于代码被编译成什么样子,我们并不需要知道。
更多的配置选项和详细的使用方法,可以参考 webpack-dev-server 的官方文档。
2)模块热替换
模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,如果模块发生替换、添加或删除,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
- 保留在完全重新加载页面期间丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 在源代码中 CSS/JS 产生修改时,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
要使用 HMR,需要做以下几步:
1)安装 webpack-dev-server
如果还没有安装 webpack-dev-server,需要首先安装它:
npm install --save-dev webpack-dev-server
# or
yarn add webpack-dev-server --dev2)更新 webpack 配置(可选)
在 webpack.config.js 文件中,需要添加 devServer 配置,并设置 hot 为 true:
const webpack = require('webpack');
module.exports = {
// ...
devServer: {
hot: true,
// ...
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// ...其他插件
],
// ...
};这里,new webpack.HotModuleReplacementPlugin() 是启用 HMR 的插件。
新版默认开启了 HMR,可以省略此步骤。
3)在应用中接受更新的模块
现在,需要在应用中接受更新的模块。使用 module.hot.accept 方法来实现这一点。
例如,如果有一个模块 print.js,可以在入口文件 index.js 中这样做:
import printMe from './print.js';
if (module.hot) {
module.hot.accept('./print.js', function() {
console.log('Accepting the updated printMe module!');
printMe();
})
}这样,当 print.js 模块更新时,它会被重新获取,并且 printMe 函数将被重新执行。
这就是使用 HMR 的基本步骤。更多的信息和详细的使用方法,可以参考 webpack 的官方文档。
二、管理配置文件
webpack-merge 是一个用于合并多个 webpack 配置对象的工具。使用 webpack-merge 可以更方便地管理和组织 webpack 配置,特别是当有多个环境(例如开发环境和生产环境)需要不同配置时。
1)安装 webpack-merge。
npm install --save-dev webpack-merge2)在项目根路径下有一个 config 目录。使用 webpack-merge 管理下面的文件。
webpack.common.conf.js
webpack.dev.conf.js
webpack.prod.conf.js3)在 webpack.common.js 内容。
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
// 其他通用配置...
};4)在 webpack.dev.conf.js 文件中使用 webpack-merge。
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const devConfig = {
mode: 'development',
entry: './src/main.js',
devServer: {
compress: true,
port: 9000,
open: true,
},
// 其他开发环境特定的配置...
};
module.exports = merge(commonConfig, devConfig);需要注意的是,webpack-merge 在合并配置对象时,如果遇到同名的属性,它将使用后面的配置覆盖前面的配置。这意味着,如果在 devConfig 中定义了和 commonConfig 中相同的属性,那么 devConfig 中的属性将会覆盖 commonConfig 中的属性。
5)在 npm scripts 中,使用合并后的配置来启动 webpack:
"scripts": {
"start": "webpack --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}为什么 entry 的值不用修改路径还没问题?
在 webpack 配置中,context 选项用于设置 webpack 运行环境的基本目录,这个目录是一个绝对路径。entry 选项则是相对于这个 context 路径的。如果没有明确设置 context,那么 webpack 默认的 context 路径就是项目的根目录。
例如,webpack 配置文件位于项目根目录的 /config/webpack.config.js 下:
module.exports = {
context: path.resolve(__dirname, 'app'),
entry: './src/main.js',
// ... 其他配置
};那么,entry 实际上指向的就是 /config/app/src/main.js。
第三章:加载器
Webpack 本身只能理解 JavaScript 和 JSON 文件。加载器使 Webpack 能够处理其他类型的文件,并将它们转换为有效的模块,可以添加到依赖图中。
一、处理样式资源
1. css-loader & style-loader
css-loader 是一个用于处理 CSS 的 webpack 插件,它会解析 CSS 文件中的 @import 和 url() 语句并将它们转换为 JavaScript 依赖。以下是如何安装和使用 css-loader 的步骤:
首先,需要在项目中安装 css-loader。可以使用 npm(Node.js 包管理器)来安装。在终端中运行以下命令:
npm install --save-dev css-loader然后,在 webpack.config.js 文件中,需要添加一个规则来告诉 webpack 如何使用 css-loader 处理 CSS 文件。在配置文件中,找到 module.rules 数组,并添加一个新的对象到这个数组中:
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.css$/i,
use: [
{loader: 'css-loader'}
],
},
// ...
],
},
// ...
};发现 HTML 页面没有 css 样式。什么原因?css-loader 只来处理 CSS 文件,还需要使用 style-loader 将 CSS 插入到 HTML 中。
注意,style-loader 是另一个需要安装的 loader,它的作用是将 CSS 添加到 DOM 中,使其生效。可以用以下命令安装:
# 1.安装 style-loader
npm install --save-dev style-loader
# 2.添加 style-loader
use: [
# 标准写法
# 执行顺序:从右到左(从下到上)
{loader: 'style-loader'},
{loader: 'css-loader'}
],简写方式。
{
test: /\.css$/i,
// 简写一:如果只有一个加载器
loader: 'css-loader'
// 简写二
use: ['style-loader', 'css-loader']
},这样,当在 JavaScript 文件中 import 一个 CSS 文件时,webpack 就会使用 css-loader 和 style-loader 来处理这个文件。
例如,如果有一个 CSS 文件 styles.css,可以在 JavaScript 文件中这样导入它:
import './styles.css';然后,当使用 webpack 打包项目时,styles.css 就会被正确地处理并添加到输出文件(./dist/index.js)中。
2. 预处理器
1)less-loader
less-loader 是 webpack 的一个加载器,它可以将 LESS 转换为 CSS。
首先,需要在项目中安装 less-loader 和 less。可以使用 npm(Node.js 包管理器)来安装。在终端中运行以下命令:
npm install --save-dev less-loader less然后,在 webpack.config.js 文件中,需要添加一个规则来告诉 webpack 如何使用 less-loader 处理 LESS 文件。在配置文件中,找到 module.rules 数组,并添加一个新的对象到这个数组中:
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.less$/i,
use: [
'style-loader',
'css-loader',
'less-loader'
],
},
// ...
],
},
// ...
};使用了 less-loader 来处理 LESS 文件,css-loader 来处理转换后的 CSS,以及 style-loader 将 CSS 插入到 HTML 中。
例如,如果有一个 LESS 文件 styles.less,可以在 JavaScript 文件中这样导入它:
import './styles.less';然后,当使用 webpack 打包你的项目时,styles.less 就会被正确地处理并添加到你的输出文件中。
2)sass-loader
1)下载包
npm install --save-dev sass-loader sasssass-loader:负责将 Sass 文件编译成 css 文件。
sass:sass-loader 依赖 sass 进行编译。
2)配置
module.exports = {
//...
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader', // 将 JS 字符串生成为 style 节点
'css-loader', // 将 CSS 转化成 CommonJS 模块
'sass-loader' // 将 Sass 编译成 CSS
]
}
]
}
};3)使用 sass 编写 css
4)在 src/index.js 中引入。
import "./sass/index.sass";
import "./sass/index.scss";3)stylus-loader
1)下载包
npm i stylus-loader -Dstylus-loader:负责将 Styl 文件编译成 CSS 文件。
2)配置
module.exports = {
//...
module: {
rules: [
{
test: /\.styl(us)?$/,
use: [
'style-loader', // 将 JS 字符串生成为 style 节点
'css-loader', // 将 CSS 转化成 CommonJS 模块
'stylus-loader' // 将 Stylus 编译成 CSS
]
}
]
}
};3)使用 styl 编写 css
4)在 src/index.js 中引入。
3. CSS 兼容性处理
PostCSS 是一个通过 JavaScript 来转换样式的工具。这个工具可以帮助我们进行一些 CSS 的转换和适配,比如自动添加浏览器前缀、css 样式的重置。但是实现这些功能,需要借助于 PostCSS 对应的插件。
PostCSS 本身不是一个加载器,而是一个用 JavaScript 工具和插件转换 CSS 代码的工具。然而,它可以通过 Webpack 的加载器(如 postcss-loader)进行集成,使得在 Webpack 构建过程中可以使用 PostCSS 来处理 CSS 文件。
如何使用 PostCSS 呢?主要就是两个步骤:
第一步:查找 PostCSS 在构建工具中的扩展,比如 webpack 中的 postcss-loader。
第二步:选择添加你需要的 PostCSS 相关的插件。
1)使用例子
首先,需要在项目中安装 PostCSS 和它的插件。可以使用 npm(Node.js 包管理器)来安装。在终端中运行以下命令:
npm install --save-dev postcss postcss-loader autoprefixer这里以 autoprefixer 插件作为例子,它会自动添加 CSS 属性的浏览器前缀。
接下来,需要在 webpack.config.js 文件中配置 postcss-loader。在配置文件中,找到 module.rules 数组,并添加一个新的对象到这个数组中:
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'autoprefixer',
{
// Options
},
],
],
},
},
},
],
},
// ...
],
},
// ...
};在这个规则中,添加了一个新的加载器 postcss-loader,并在 options 中配置了 autoprefixer 插件。
现在,当在 JavaScript 文件中 import 一个 CSS 文件时,webpack 就会使用 style-loader、css-loader 和 postcss-loader 来处理这个文件。并且,postcss-loader 会使用 autoprefixer 插件自动添加 CSS 属性的浏览器前缀。
例如,如果有一个 CSS 文件 styles.css,在 JavaScript 文件中这样导入它:
import './styles.css';然后,当使用 webpack 打包你的项目时,styles.css 就会被正确地处理并添加到你的输出文件中。
问题:webpack.config.js 好臃肿,这还只是一个 autoprefixer 插件的配置。
创建一个 PostCSS 配置文件 postcss.config.js 在项目的根目录下:
module.exports = {
plugins: [
require('autoprefixer')
]
}2)postcss-preset-env
事实上,在配置 postcss-loader 时,我们配置插件并不需要使用 autoprefixer。可以使用另外一个插件:postcss-preset-env。
postcss-preset-env 也是一个 postcss 的插件。它可以帮助我们将一些现代的 CSS 特性,转成大多数浏览器认识的 CSS,并且会根据目标浏览器或者运行时环境添加所需的 autoprefixer(相当于已经内置了 autoprefixer)。
[1] postcss.config.js
使用 npm 安装它:
npm install --save-dev postcss-preset-env然后,可以在 postcss.config.js 文件中配置它:
module.exports = {
plugins: [
// 标准写法
require('postcss-preset-env')({
// Options
}),
// 简写
'postcss-preset-env'
],
};在这个配置中,可以根据需要提供选项。例如可以指定你的目标浏览器:
module.exports = {
plugins: [
require('postcss-preset-env')({
browsers: 'last 2 versions',
}),
],
};在这个配置中,postcss-preset-env 只需要支持每个浏览器的最后两个版本。
最后,在构建工具(如 webpack)中配置 PostCSS。
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
],
},
};在这个配置中,所有的 CSS 文件都会被 postcss-loader 处理,postcss-loader 会使用你在 postcss.config.js 中配置的插件。
[2] webpack.config.js
如果不想有 postcss.config.js 文件,可以在 webpack.config.js 配置。
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
clean: true,
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
],
},上面代码不好,比如还有 less、sass 等,每次都要写一次这样的代码,怎么办?
// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {
return [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
preProcessor,
].filter(Boolean);
};
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
clean: true,
},
module: {
rules: [
{
// 用来匹配 .css 结尾的文件
test: /\.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: getStyleLoaders(),
},
{
test: /\.less$/,
use: getStyleLoaders("less-loader"),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders("sass-loader"),
},
{
test: /\.styl$/,
use: getStyleLoaders("stylus-loader"),
},
},
};然后,控制兼容性。
可以在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度。
{
// 其他省略
"browserslist": ["ie >= 8"]
}想要知道更多的 browserslist 配置,查看 browserslist 文档
以上为了测试兼容性所以设置兼容浏览器 ie8 以上。
实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:
{
// 其他省略
"browserslist": ["last 2 version", "> 1%", "not dead"]
}解释:
"last 2 versions"表示你想要支持所有浏览器的最后两个版本。"> 1%"表示你想要支持全球使用率超过 1% 的所有浏览器。"not dead"表示你不想支持已经官方声明不再维护的死掉的浏览器。
二、处理图片资源
Webpack 5 引入了一种新的模块类型,称为 Asset Modules,用来替代之前的 file-loader、url-loader 和 raw-loader。Asset Modules 是一种模块类型,允许使用资源文件(如字体、图像等)而无需通过额外的 loader。
Asset Modules 有四种类型:asset(替代 url-loader 和 file-loader)、asset/resource(替代 file-loader)、asset/inline(替代 url-loader)和 asset/source(替代 raw-loader)。
asset: 在导出一个 data URI(即base64编码字符串)或者发送一个单独的文件到输出目录,并在导出处返回文件的 URL 之间自动选择。当文件大小小于配置的限制时,会选择 data URI。asset/resource: 发送一个单独的文件到输出目录,并在导出处返回文件的 URL。这与file-loader的工作方式类似。asset/inline: 导出一个资源的 data URI(base64 编码)。这与url-loader的工作方式类似。asset/source: 导出资源的源代码(把文件内容导入到某个变量)。这与raw-loader的工作方式类似。
可以在 module.rules 中配置这些类型。例如,可以使用 asset 模块类型来处理图像文件:
module.exports = {
// ...
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8kb
},
},
},
],
},
// ...
};在这个配置中,当一个 .png、.jpg、.jpeg 或 .gif 文件小于 8kb 时,webpack 会将它作为 data URI 导出,否则,webpack 会将它作为单独的文件发送到输出目录,并在导出处返回文件的 URL。
修改打包后的图片名与路径
在 Webpack 5 中,可以使用 assetModuleFilename 选项来自定义 Asset Modules 的输出文件名。这个选项可以在 output 配置中设置。例如,如果你希望所有的图像文件都被放入 images 目录,并且文件名包含原始文件名和内容哈希,你可以这样配置:
module.exports = {
// ...
output: {
// ...
assetModuleFilename: 'images/[name][hash][ext]',
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
type: 'asset/resource',
},
],
},
// ...
};在这个配置中,[name] 是原始文件名,[ext] 是文件扩展名,[hash] 是基于文件内容的哈希。
注意,这个配置仅适用于 asset/resource 类型的模块。对于 asset 或 asset/inline 类型的模块,文件名的配置是不相关的,因为这些模块类型会生成 data URI,而不是实际的文件。
另外,你也可以在每个 loader 规则中单独设置 assetModuleFilename,这样就可以对不同类型的资源采用不同的命名策略。例如:
module.exports = {
// ...
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
type: 'asset/resource',
generator: {
// 将图片文件命名
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
filename: 'images/[name][hash][ext]',
},
},
{
test: /\.svg$/i,
type: 'asset/resource',
generator: {
filename: 'icons/[name][hash:8][ext]',
},
},
],
},
// ...
};在这个配置中,图像文件会被放入 images 目录,而 .svg 文件会被放入 icons 目录。
三、处理字体图标资源
一句话:字体图标也是静态资源。
1)项目添加字体文件与 CSS 文件。
2)src/index.js 中引入上一步的 CSS 文件。
import "./css/iconfont.css";3)配置
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},4)使用
处理视频与音频等资源,也是这样处理的。在
test: /\.(ttf|woff2?)$/,代码后面继续添加test: /\.(ttf|woff2?|map4|map3|avi)$/,。
四、Babel
1. 是什么
Babel 是一个广泛使用的 JavaScript 编译器,它可以将标准的、新版的 JavaScript(ES6, ES7, ES8 等)转换为旧版的 JavaScript(ES5 或更早),以确保你的代码可以在旧版本的浏览器或环境中运行。
2. 配置文件
方法一:新建文件,位于项目根目录 babel.config.*
babel.config.jsbabel.config.json
方法二:新建文件,位于项目根目录 .babelrc.*
.babelrc.babelrc.js.babelrc.json
方法三:不需要创建文件,在原有文件 package.json 基础上添加 babel 键。
Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可。
3. 具体配置
以 babel.config.js 配置文件为例:
module.exports = {
// 预设
presets: [],
};presets 预设
简单理解:就是一组 Babel 插件,扩展 Babel 功能。
@babel/preset-env: 一个智能预设,允许您使用最新的 JavaScript。@babel/preset-react:一个用来编译 React jsx 语法的预设。@babel/preset-typescript:一个用来编译 TypeScript 语法的预设。
4. 使用
1)babel-loader
babel-loader 是 webpack 的一个加载器,用于在 webpack 构建过程中将 Babel 和 webpack 集成在一起。通过使用 babel-loader,webpack 可以在打包 JavaScript 文件之前,先通过 Babel 进行转译。
首先,需要安装 babel-loader 和 Babel 的核心库。可以使用 npm(或 yarn)来进行安装:
npm install --save-dev @babel/core babel-loader然后,想让箭头函数转换为普通函数,const 转成 var。怎么办?
npm install @babel/plugin-transform-arrow-functions -D
npm install @babel/plugin-transform-block-scoping -D接着,需要在 webpack 配置文件(通常是 webpack.config.js)中添加一个规则,以使用 babel-loader 处理 JavaScript 文件。
module.exports = {
module: {
rules: [
{
test: /\.js$/, // 使用正则来匹配 js 文件
exclude: /node_modules/, // 排除 node_modules 目录
use: {
loader: 'babel-loader', // 指定使用的 loader
options: {
plugins: [
[
"@babel/plugin-transform-arrow-functions",
// 将 ES6 的块级作用域转换为 ES5 代码
"@babel/plugin-transform-block-scoping"
],
],
},
}
}
]
}
};也可以把上面代码抽取到 babel.config.js 文件里。
module.exports = {
plugins: [
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-block-scoping"
]
};2)@babel/preset-env
@babel/preset-env 是一个 Babel 预设,它可以根据你的目标环境自动确定你需要的 Babel 插件和 polyfills(降级 JS)。这意味着你不再需要手动指定转换 ES2015(或更高版本)语法的插件,@babel/preset-env 会根据你的配置自动处理这些事情。
要使用 @babel/preset-env,需要首先安装它。可以使用 npm 或 yarn 来安装。在项目目录中打开终端,然后运行以下命令:
使用 npm:
npm install --save-dev @babel/preset-env或者,使用 yarn:
yarn add --dev @babel/preset-env安装完成后,可以在 webpack.config.js 文件中配置它,如下:
module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
options: {
presets: [
'@babel/preset-env',
// other presets...
],
}
},
],
},
// ...
};还可以在 Babel 配置文件 babel.config.js 中指定 @babel/preset-env。
可以将 @babel/preset-env 添加到 presets 数组中,如下所示:
module.exports = {
presets: [
'@babel/preset-env',
// other presets...
],
// plugins...
};默认情况下,@babel/preset-env 会转换所有 ES2015-ES2020 语法到 ES5。然而,可以通过配置目标环境或者浏览器来定制化 @babel/preset-env 的行为。例如,可以指定目标浏览器的版本,这样 Babel 只会转换那些不被这些浏览器版本支持的语法。
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
"chrome": "58",
"ie": "11"
}
}
],
// other presets...
],
// plugins...
};在这个例子中,@babel/preset-env 会只转换那些 Chrome 58 和 IE 11 不支持的语法。
第四章:插件
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
一、CleanWebpackPlugin
clean-webpack-plugin 是一个用来清理 webpack 的构建目录(通常是 dist 或 build)的插件。在每次构建前,这个插件都会清理构建目录,以确保只有使用的文件被包含在构建目录中。
首先,需要安装这个插件。可以使用 npm 或 yarn 来安装:
npm install --save-dev clean-webpack-plugin
# or
yarn add clean-webpack-plugin --dev然后,在 webpack 配置文件中,需要引入并使用这个插件:
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
// ...
plugins: [
new CleanWebpackPlugin(),
// ...其他插件
],
// ...
};在这个配置中,CleanWebpackPlugin 在每次构建前都会清理 webpack 的输出目录(默认是 output.path 指定的目录,通常是 dist 或 build)。
更多的配置选项和详细的使用方法,可以参考 clean-webpack-plugin 的官方文档。
目前基本不在使用这个插件了,因为 webpack 已经有这个功能了。只需在 webpack 配置文件中加入下面的内容。
module.exports = {
// ...
output: {
clean: true, // 在生成文件之前清空 output 目录
},
};二、HtmlWebpackPlugin
HtmlWebpackPlugin 是一个用于生成 HTML 文件的 webpack 插件,它可以接收一个 HTML 文件作为模板,然后将 webpack 打包后生成的 js、css 文件自动注入到这个 HTML 文件中。
首先,需要安装这个插件。可以使用 npm 或 yarn 来安装:
npm install --save-dev html-webpack-plugin
# or
yarn add html-webpack-plugin --dev然后,在 webpack 配置文件中,需要引入并使用这个插件:
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ......
plugins: [
new HtmlWebpackPlugin({
title: "学习 webpack",
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点: 1.内容和源文件一致 2.自动引入打包生成的js等资源
template: path.resolve(__dirname, "public/index.html"),
}),
// ...其他插件
],
// ......
};在这个配置中,HtmlWebpackPlugin 会使用 public/index.html 作为模板,生成一个新的 HTML 文件,并将 webpack 打包后的 js、css 文件自动注入到这个 HTML 文件中。
HtmlWebpackPlugin 还有许多其他的配置选项,例如:
filename:指定生成的 HTML 文件的名称。inject:决定 script 标签应该放在head还是body中。minify:压缩生成的 HTML 文件。hash:为所有包含的 js 和 css 文件添加唯一的 webpack 编译哈希。当设置
hash: true,生成的 HTML 文件中引用 js 和 css 文件的链接会附带一个查询参数,例如main.js?d587bbd6e38337f5accd。这个查询参数就是 webpack 编译哈希。
例如:
new HtmlWebpackPlugin({
template: 'src/index.html',
filename: 'index.html',
inject: 'body',
minify: true,
hash: true,
})更多的配置选项和详细的使用方法,可以参考 html-webpack-plugin 的官方文档。
三、DefinePlugin
DefinePlugin 是一个 webpack 内置的插件,它允许在编译时创建全局常量,这在需要区分开发和生产环境的不同行为时非常有用。
首先,需要在 webpack 配置文件中引入这个插件:
const webpack = require('webpack');
module.exports = {
// ......
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
'VERSION': JSON.stringify('5fa3b9'),
'BROWSER_SUPPORTS_HTML5': true,
'TWO': '1+1',
}),
// ...其他插件
],
// ......
};在这个配置中,DefinePlugin 创建了四个全局常量:process.env.NODE_ENV、VERSION、BROWSER_SUPPORTS_HTML5 和 TWO。它们在编译时被替换为对应的值。
注意,由于这些值是在编译时直接插入到源代码中的,所以如果值是字符串,需要使用 JSON.stringify 来确保它们被正确地插入到代码中。
例如,'process.env.NODE_ENV': JSON.stringify('production') 会被替换为 process.env.NODE_ENV = 'production',而不是 process.env.NODE_ENV = production。
另外,DefinePlugin 插件中的值在编译时是直接插入到源代码中的,所以它们应该是源代码的一部分,而不是一个独立的表达式。例如,'TWO': '1+1' 会被替换为 TWO = 1+1,而不是 TWO = 2。虽然值在编译时是直接插入到源代码中的,但是在源码执行时,还是把 'TWO': '1+1' 会被替换为 TWO = 2。如果不想这样,就需要在套一个双引号,例如 "'1+1'"。
更多的使用方法和详细的信息,可以参考 webpack 的官方文档。
四、Eslint
用来检测 js 和 jsx 语法的工具,可以配置各项功能。
使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,将来运行 Eslint 时就会以写的规则对代码进行检查。
1. 配置文件
方法一:新建文件 .eslintrc.*,位于项目根目录。
.eslintrc
.eslintrc.js
.eslintrc.json区别在于配置格式不一样。
方法二:package.json 中 eslintConfig
不需要创建文件,在原有文件基础上写。
ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可。
2. 具体配置
以 .eslintrc.js 配置文件为例:
module.exports = {
// 解析选项
parserOptions: {},
// 具体检查规则
rules: {},
// 继承其他规则
extends: [],
// ...
// 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};parserOptions 解析选项
parserOptions: {
ecmaVersion: 6, // ES 语法版本
sourceType: "module", // ES 模块化
ecmaFeatures: { // ES 其他特性
jsx: true // 如果是 React 项目,就需要开启 jsx 语法
}
}rules 具体规则
"off"或0- 关闭规则"warn"或1- 开启规则,使用警告级别的错误:warn(不会导致程序退出)"error"或2- 开启规则,使用错误级别的错误:error(当被触发的时候,程序会退出)
rules: {
semi: "error", // 要求在每个语句的末尾都必须使用分号
'array-callback-return': 'warn', // 强制数组方法的回调函数中有 return 语句,否则警告
'default-case': [
'warn', // 要求 switch 语句中有 default 分支,否则警告
{ commentPattern: '^no default$' } // 允许在最后注释 no default, 就不会有警告了
],
eqeqeq: [
'warn', // 强制使用 === 和 !==,否则警告
'smart' // https://eslint.nodejs.cn/docs/rules/eqeqeq#smart 除了少数情况下不会有警告
],
}更多规则详见:规则文档
extends 继承
开发中一点点写 rules 规则太费劲了,所以有更好的办法,继承现有的规则。
现有以下较为有名的规则:
- Eslint 官方的规则:
eslint:recommended - Vue Cli 官方的规则:
plugin:vue/essential - React Cli 官方的规则:
react-app
这些规则使用前都需要先安装。
// 例如在React项目中,我们可以这样写配置
module.exports = {
extends: ["react-app"],
rules: {
// 我们的规则会覆盖掉react-app的规则
// 所以想要修改规则直接改就是了
eqeqeq: ["warn", "smart"],
},
};3. 在 Webpack 中使用
1)下载包
npm i eslint-webpack-plugin eslint -D2)定义 Eslint 配置文件 .eslintrc.js
module.exports = {
// 继承 Eslint 规则
extends: ["eslint:recommended"],
env: {
node: true, // 启用node中全局变量
browser: true, // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6,
sourceType: "module",
},
rules: {
"no-var": 2, // 不能使用 var 定义变量
},
};3)修改 js 文件代码 main.js
import count from "./js/count";
import sum from "./js/sum";
// 引入资源,Webpack才会对其打包
import "./css/iconfont.css";
import "./css/index.css";
import "./less/index.less";
import "./sass/index.sass";
import "./sass/index.scss";
import "./styl/index.styl";
// 新添加的
var result1 = count(2, 1);
console.log(result1);
var result2 = sum(1, 2, 3, 4);
console.log(result2);4)与 webpack 集成
webpack.config.js
// 1.引入插件
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
// 2.添加插件到 webpack 中
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src"),
}),
],5)运行指令
npx webpack在控制台查看 Eslint 检查效果。
添加的 Eslint 只有在打包才会显示错误,可以让其在编写代码的时候就显示错误吗?
VSCode Eslint 插件
打开 VSCode,安装 Eslint 插件,即可不用编译就能看到错误,可以提前解决。
但是此时就会对项目所有文件默认进行 Eslint 检查了,我们 dist 目录下的打包后文件就会报错。但是我们只需要检查 src 下面的文件,不需要检查 dist 下面的文件。
所以可以使用 Eslint 忽略文件解决。在项目根目录新建下面文件:
.eslintignore# 忽略dist目录下所有文件 dist
五、对 CSS 处理
1. 提取 CSS 成单独文件
CSS 文件目前被打包到 js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式。
这样对于网站来说,会出现闪屏现象,用户体验不好。
我们应该是单独的 CSS 文件,通过 link 标签加载性能才好。
1)下载包
npm i mini-css-extract-plugin -D2)配置
webpack.prod.js
大致思路:① 引入插件;② 把 style-loader 换为 MiniCssExtractPlugin.loader;③ 注入插件。
// 1.引入插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
// ......
module: {
rules: [
{
test: /\.css$/,
// 2.把 style-loader 换为 MiniCssExtractPlugin.loader
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
},
plugins: [
// 3.提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: "static/css/main.css",
}),
],
mode: "production",
};2. CSS 压缩
1)下载包
npm i css-minimizer-webpack-plugin -D2)配置
webpack.prod.js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
// ......
plugins: [
// 提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: "static/css/main.css",
}),
// css压缩
new CssMinimizerPlugin(),
],
// ......
};