前言
Webpack 想要实现的是整个前端项目的模块化,项目中的各种资源(包括 CSS 文件、图片等)都应该属于需要被管理的模块。换句话说, Webpack 不仅是 JavaScript 模块打包工具,还是整个前端项目(前端工程)的模块打包工具。也就是说,我们可以通过 Webpack 去管理前端项目中任意类型的资源文件。
因为 Webpack 实现不同种类资源模块加载的核心就是 Loader,所以今天我来和你聊聊 Webpack 的 Loader 机制。
CSS样式loader
首先,我们尝试通过 Webpack 打包项目中的一个 CSS 文件,由此开始探索 Webpack 是如何加载资源模块的?
main.css:
h2 {position: absolute;left: 50%;transform: translate(0, -50%);color: red;}
因为我们要尝试打包相应的css文件,所以在index.js文件中引入main.css一同打包:
import create from "./heading";//引入cssimport "./css/main.css";const heading = create();document.body.append(heading);
下面我们尝试直接打包,显示错误:
错误信息大体的意思是说,在解析模块过程中遇到了非法字符,而且错误出现的位置就是在我们的 CSS 文件中。
出现这个错误的原因是因为 Webpack 内部默认只能够处理 JS 模块代码,也就是说在打包过程中,它默认把所有遇到的文件都当作 JavaScript 代码进行解析,但是此处我们让 Webpack 处理的是 CSS 代码,而 CSS 代码是不符合 JavaScript 语法的,所以自然会报出模块解析错误。
这里有一个非常重要的提示:You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. (我们需要用适当的加载器来处理这种文件类型,而当前并没有配置一个可以用来处理此文件的加载器)。
根据这个错误说明,我们发现 Webpack 是用 Loader(加载器)来处理每个模块的,而内部默认的 Loader 只能处理 JS 模块,如果需要加载其他类型的模块就需要配置不同的 Loader。这也就引出了我们今天的主角:Loader。
loader 的使用方式
处理上面的错误需要的是一个可以加载 CSS 模块的 Loader,最常用到的是 css-loader。我们需要通过 npm 先去安装这个 Loader,然后在配置文件中添加对应的配置,具体操作和配置如下所示:
cnpm install --save-dev css-loader
修改webpack配置文件:
...module.exports = {...module: {rules: [{test: /\.css$/,use: 'css-loader'},],}}
在配置对象的 module 属性中添加一个 rules 数组。这个数组就是我们针对资源模块的加载处理的规则配置,其中的每个规则对象都需要设置两个属性:
- 首先是 test 属性,它是一个正则表达式,用来匹配打包过程中所遇到文件路径,这里我们是以 .css 结尾;
- 然后是 use 属性,它用来指定匹配到的文件需要使用的 loader,这里用到的是 css-loader。
配置完成过后,我们回到命令行终端重新运行打包命令,打包过程就不会再出现错误了,因为这时 CSS 文件会交给 css-loader 处理过后再由 Webpack 打包。样式模块加载问题
修改上面配置文件之后,我们再次打包:
成功打包css文件,但是当我们运行index.html文件时,css并没有起作用。结果显示
根据我们的main.css,上面“打包尝试”应该水平居中且字体为红色。因此这里的main.css并没有起作用。
下面我们来分析产生这个问题的真正原因,首先,我们找到刚刚生成的 webpack.js 文件,因为这个文件是 Webpack 打包后的结果,所有的模块都应该在这个文件中出现。
我们在webpack.js中找到css打包后对应的函数:
仔细阅读这个文件,我们会发现 css-loader 的作用是将 CSS 模块转换为一个 JS 模块,具体的实现方法是将我们的 CSS 代码 push 到一个数组中,这个数组是由 css-loader 内部的一个模块提供的,但是整个过程并没有任何地方使用到了这个数组。
因此这里样式没有生效的原因是: css-loader 只会把 CSS 模块加载到 JS 代码中,而并不会使用这个模块。
所以这里我们还需要在 css-loader 的基础上再使用一个 style-loader,把 css-loader 转换后的结果通过 style 标签追加到页面上。
安装style-loader:
cnpm install style-loader --save-dev
安装完 style-loader 之后,我们将配置文件中的 use 属性修改为一个数组,将 style-loader 也放进去。这里需要注意的是,一旦配置多个 Loader,执行顺序是从后往前执行的,所以这里一定要将 css-loader 放在最后,因为必须要 css-loader 先把 CSS 代码转换为 JS 模块,才可以正常打包,具体配置如下:
const path = require('path')module.exports = {entry: './src/index.js',mode: 'none',output: {filename: 'main.js',path: path.join(__dirname, 'dist')},module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader']},],}}
配置完成之后,再次回到命令行重新打包,此时 webpack.js 文件中会额外多出两个模块。篇幅的关系,我们这里不再仔细解读。style-loader 的作用总结一句话就是,将 css-loader 中所加载到的所有样式模块,通过创建 style 标签的方式添加到页面上。
**
图片文件的处理loader
理图片有两个loader可用:
- url-loader (像 file loader 一样工作,但如果文件小于限制,可以返回 data URL)
- file-loader (将文件发送到输出文件夹,并返回(相对)URL)
在这里我们先提前安装两个依赖:
cnpm install url-loader file-loader --save-dev
关键点:当图片大小小于limit属性设置值时,可以使用url-loader,但当图片过大,还是使用file-loader。
**
我们尝试使用图片资源,style.css文件:
h2 {position: absolute;left: 50%;transform: translate(0, -50%);color: red;}.container {margin: 100px auto;height: 200px;background: url("../assets/webpack.png") no-repeat;}
index.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack </title>
</head>
<body>
<div class="container"></div>
<script src="dist/main.js"></script>
</body>
</html>
运行 npx webpack 会发现有一个图片资源处理失败的提示,如下图:

我们添加图片资源处理 loader,webpack.config.js 文件:
const path = require('path')
module.exports = {
entry: './src/index.js',
mode: 'none',
output: {
filename: 'main.js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpg|gif)$/,
use: [
{ loader: 'url-loader', options: {limit: 13000} },
],
},
],
}
}
打包后运行效果如下图所示:
我们再把 webpack.config.js 的限制大小处理调小,重新打包后,发现打包失败,由于图片过大,无法使用url-loader 而又找不到 file-loader。,且少 file-loader,
{
test: /\.(png|jpg|gif)$/,
use: [
{ loader: 'url-loader', options: {limit: 1024} },
],
},
为什么要在 JS 中加载其他资源
说到这里,你可能会产生疑惑:Webpack 为什么要在 JS 中载入 CSS 呢?不是应该将样式和行为分离么?
其实 Webpack 不仅是建议我们在 JavaScript 中引入 CSS,还会建议我们在代码中引入当前业务所需要的任意资源文件。因为真正需要这个资源的并不是整个应用,而是你此时正在编写的代码。
常用的 loader
| 名称 | 链接 |
|---|---|
| file-loader | https://webpack.js.org/loaders/file-loader/ |
| url-loader | https://webpack.js.org/loaders/url-loader/ |
| babel-loader | https://webpack.js.org/loaders/babel-loader/ |
| style-loader | https://webpack.js.org/loaders/style-loader/ |
| css-loader | https://webpack.js.org/loaders/css-loader/ |
| sass-loader | https://webpack.js.org/loaders/sass-loader/ |
| postcss-loader | https://webpack.js.org/loaders/postcss-loader/ |
| eslint-loader | https://github.com/webpack-contrib/eslint-loader |
| vue-loader | https://github.com/vuejs/vue-loader |
其他 loader 使用
sass-loader
新增一个 sass 样式文件并且在 index.js 文件中引用,执行 npx webpack 构建命令。
test.scss
$bg: yellow;
body {
background-color: $bg;
}
// src/index.js
import "./css/test.scss";
会发现没有找到对应的 loader 处理报错,如下图:

处理上面的错误需要的是一个可以加载 sass 模块的 Loader,最常用到的是 sass-loader。我们需要通过 npm 先去安装这个 Loader,然后在配置文件中添加对应的配置,具体操作和配置如下所示:
cnpm install sass sass-loader --save-dev
webpack.config.js 配置
module.exports = {
module: {
rules: [
{
test: /\.(c|sa|sc)ss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
...
],
}
}
postcss-loader
有时候,我们写的一些 CSS 样式高级新特性,需要兼容包不同版本浏览器的样式,例如 display: flex; 在部分浏览器的写法是不一样的,为了提高效率,我们可以使用 postcss-loader 进行样式的 autoprefixer。
我们需要安装 postcss-loader 和 postcss:
cnpm install --save-dev postcss-loader postcss
给 test.scss 增加 样式,如下:
$bg: yellow;
body {
background-color: $bg;
}
.container {
display: flex;
}
修改 webpack.config.js 配置
module.exports = {
...
module: {
rules: [
{
test: /\.(c|sa|sc)ss$/,
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
},
]
}
上述只是配置了 postcss-loader 处理 css 资源,如果需要进行 autoprefixer 处理,还需要一个插件:postcss-preset-env,并且进行 postcss 的配置。
cnpm install postcss-preset-env --save-dev
根目录新建 postcss.config.js,并且进行配置,具体如下:
module.exports = {
plugins: [
[
"postcss-preset-env",
],
],
};
为了明确对应明确兼容浏览器的范围,需要进行兼容浏览器列表的配置。在根目录新建 .browserslistrc 文件,配置如下:
> 1%
last 2 versions
not ie <= 8
拓展:
- 浏览器分析的数据来源:https://caniuse.com/usage-table
babel-loader
资源模块
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
在 webpack 5 之前,通常使用:
raw-loader将文件导入为字符串url-loader将文件作为 data URI 内联到 bundle 中file-loader将文件发送到输出目录
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
asset/resource发送一个单独的文件并导出 URL。之前通过使用file-loader实现。asset/inline导出一个资源的 data URI。之前通过使用url-loader实现。asset/source导出资源的源代码。之前通过使用raw-loader实现。asset在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用url-loader,并且配置资源体积限制实现。
下面我们来使用资源模块加载资源,之前的 url-loader 配置可以改用一下的配置
module.exports = {
...
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
// use: [
// { loader: 'url-loader', options: {limit: 1024} },
// ],
type: 'asset',
// 设置文件名
generator: {
filename: 'static/[hash:6][ext][query]'
},
parser: {
// url-loader 的 limit 设置
dataUrlCondition: {
maxSize: 3 * 1024 // 4kb
}
}
},
],
}
}
资料
