「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战」
前面我们用 webpack 打包的都是 JS 文件,如果我们还想对 CSS 文件进行打包呢?
下面,我们就用一个案例来讲解 webpack 对 CSS 文件的打包。
上一节的最后,我们在 element.js 文件中创建了一个 <div> 元素,并且给它添加了 title 类。现在,我们想给这个 title 类设置对应的样式,并且是在 CSS 文件中编写样式。比如,我们在项目目录下的 src 目录下新建 css 目录,然后在 css 目录下新建 style.css 文件,向该文件中添加如下样式代码:
.title {
color: red;
font-size: 30px;
font-weight: 700;
}
现在,./src/css 下已经有 style.css 这个 css 文件了,那么请问,现在运行打包命令,这个 css 文件会被打包吗?答案是不会。因为上一节我们已经讲过,webpack 是根据它的依赖图来打包模块的。而我们这个 style.css 文件当前并没有加入到依赖图中,因为从入口(./src/main.js)开始找,最终并没有找到这个文件。
但我们是希望对这个文件进行打包的,那该怎么做呢?我们可以在 ./src/main.js 中引入这个 css 文件:
import { sum } from './js/math.js';
const { formatPrice } = require('./js/format.js');
import './js/element';
import './css/style.css'; // 导入 style.css 文件
...
这样一来,作为打包入口的 main.js 文件就依赖 style.css 这个文件了,到时候也会对这个 css 文件进行打包。当然,我们也可以不在 main.js 中导入这个 css 文件,因为这个 css 文件其实和 ./js/element.js 文件的关联性更强,所以我们可以在 ./js/element.js 中导入这个 css 文件:
import '../css/style.css'; // 导入 style.css 文件
const el = document.createElement('div');
...
注意导入时的路径别写错了,不然打包时会报如下格式的错误:
Module not found: Error: Can't resolve xxx in xxx。
这样一来,作为打包入口的 main.js 文件依赖 element.js,而 element.js 又依赖 style.css,所以最终 style.css 文件也会被打包。
好,那我们运行 npm run build 命令进行打包:
你会发现,打包失败了,出现了一个错误,大意是:./src/css/style.css 这个模块(在 webpack 中,一个文件可以看成一个模块)解析失败了,你可能需要一个合适的加载器(loader)来处理这种文件类型(这里指 css 文件类型),而当前没有配置加载器去处理这个文件。
就是说当前 webpack 不知道如何处理 css 文件,你可能会问:那之前我们打包 js 文件怎么就可以呢?这是因为 webpack 默认就支持 CommonJS、AMD、ESM 等模块类型[^2],所以才默认支持 js 文件。而 css 文件 webpack 本身默认是不支持的。我们要知道,webpack 事实上是一个很大的生态,除了它本身的核心代码,还包括了 loaders、plugins 等很多其它东西,这些东西一起造就了强大的 webpack。而在这个庞大的生态中,就有能够帮助我们处理 css 文件类型的 loader:css-loader(我们常说 webpack 能够打包 css 文件的原因就是它已经有帮助我们处理 css 的 loader 了)。所以,我们得有对应的 loader 来处理 css,webpack 才能对 css 文件进行打包。
-
loader是什么?loader可以用于对模块的源代码进行转换(解析);- 我们可以将
css文件也看成是一个模块,我们是通过import来加载这个模块的; - 在加载这个模块时,
webpack其实并不知道如何对其进行加载,我们必须指定对应的loader来完成这个功能;
-
我们需要一个怎样的
loader呢?- 对于加载
css文件来说,我们需要一个可以读取css文件的loader; - 这个
loader最常用的是css-loader; - 当然,你也可以自己编写一个对应的
loader;
- 对于加载
-
我们选择使用
css-loader,需要先去下载了安装它:-
因为我们已经有了
npm这个包管理工具,所以我们可以直接使用npm来安装它:npm install -D css-loader因为我们这里加载
css文件的需求只在开发阶段有,所以使用-D标志作为开发时依赖进行安装。
-
安装完 css-loader 后,我们还不能马上打包,因为 webpack 没有那么智能,我们还需要告诉它在加载 css 文件的时候使用 css-loader。webpack 不会自动帮我们找到 css-loader,因此我们必须手动指定在加载 css 文件时让 webpack 使用 css-loader,甚至使用多个 loader(而且多个 loader 之间还有顺序要求)。所以,我们就要去进行配置了,既然是配置,就要想到去 webpack.config.js 文件中进行配置。
不过,使用 css-loader 来加载 css 文件的方式其实有 3 种:
-
内联方式:内联方式使用较少,因为不方便管理,其用法如下:
在引入的样式文件前加上要使用的
loader,并在loader和样式文件之前使用!分隔:import 'css-loader!../css/style.css';上面代码的意思是在导入
../css/style.css文件时,使用css-loader对它进行加载。 -
CLI方式(webpack5中不再使用):- 在
webpack5的文档中已经没有--module-bind了; - 实际应用中也比较少使用,因为不方便管理;
- 在
-
配置方式:
- 配置方式的意思是在
webpack.config.js文件中写明配置信息:module.rules中允许我们配置多个loader(因为我们还会继续使用其它的loader,来完成其它文件的加载);- 这种方式可以更好地表示
loader的配置,方便后期维护,也让你对各个loader有一个全局的概览;
module.rules的配置如下:rules属性对应的值是一个数组:[Rule]- 数组中存放的是一个个的
Rule对象,Rule对象中可以设置多个属性:test属性:用于对资源(resource)进行匹配,通常会设置成正则表达式;use属性:对应的值是一个数组:[UseEntry]UseEntry是一个对象,可以通过对象的属性来设置一些其它属性loader属性:必须有,对应的值是一个字符串;options属性:可选的属性,值是一个字符串或对象,值会被传入到loader中;query属性:目前已经使用options属性来替代;
- 传递字符串(如:
use: ['style-loader'])是只设置loader属性时的UseEntry对象的简写方式(如:use: [{ loader: 'style-loader' }]);
loader属性:使用loader属性是Rule.use: [{ loader }]的简写方式;
- 配置方式的意思是在
使用配置方式来指定 webpack 使用 css-loader 来加载 css 文件的做法如下:
-
在
webpack.config.js文件中,来到module.exports对应的对象中,之前我们已经配置了打包的入口(entry)和出口(output),现在我们还可以添加模块(module,前面说过,一个css文件就是一个模块)的配置:const path = require('path'); module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, './build'), filename: 'bundle.js' }, module: { rules: [ { test: /\.css$/, // 正则表达式 loader: 'css-loader' // 写法一 } ] } }module.rules属性对应的值是一个数组,因为我们以后可能会有多个规则(rule)要配置;上面规则对象中的
test属性对应的值的意思是:只要是以.css结尾的模块(文件),就会被匹配上当前的规则,就会使用当前规则中的loader。需要注意正则表达式中的.具有特殊的含义(会匹配任意字符),我们这里想要匹配.这个字符,需要使用\对它进行转义;上面规则对象中的
loader: 'css-loader'其实是一种语法糖,也可以写成use: 'css-loader',但一般我们在使用use时会使用数组:// 写法二 use: [ 'css-loader' ]为什么要使用数组呢?因为在这里我们不确定在加载
css模块时仅使用一个loader就能搞定,事实上,加载css模块时仅使用css-loader达不到最终的效果,你会发现页面上的样式并没有生效,因为我们还需要其它的loader把我们用css-loader加载出来的css代码通过<style>元素添加到DOM文档中,才能让样式真正生效。所以,通过数组,我们就可以使用多个loader了。但是,我们在使用某个
loader时,也可能需要给它传递一些参数,因此,use属性真正完整的写法是这样的:use: [ { loader: 'css-loader', options: { ... } } ]但我们这里没有需要传递参数的需求,所以可以不加
options属性:// 写法三 use: [ { loader: 'css-loader' } ]而当我们没有传递参数的需求时,我们一般就会这样写:
use: [ 'css-loader' ]
我们通过内联方式或配置方式(常用)指定好 webpack 在遇到 css 文件时使用 css-loader 这个 loader 对文件进行加载后,再来运行 npm run build 命令进行打包:
可以看到,除了一个 warning 信息(目前我们先忽略它),之前的报错已经没有了,这就说明 css 文件已经被成功加载了(成功加载但并没有生效,页面上还没有出现对应的效果,这涉及到另外一个原因,我们后面再说)。
注意:在使用配置方式指定 loader 的使用时,需要把前面通过内联方式添加的代码删掉(要还原为使用内联方式之前的代码),否则打包会报错:
以上,就是关于 loader(css-loader)的基本配置过程,根据不同的情况,我们会使用不同的写法。