这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战
接上一篇文章,下面,我们来编写一个案例代码(本案例是在官方文档案例的基础上修改而来)。
- 创建一个
component.js
文件- 通过
JavaScript
创建一个元素,并且希望给它设置一些样式;
- 通过
好,我们先在项目的 ./src/js/
路径下创建 component.js
文件,文件内容如下:
function component() {
const element = document.createElement('div');
// 这里是有意使用了这样一个数组形式的案例,因为我们后面还会讲 lodash 的打包,
// 就是 lodash 到底会不会和其它代码打包到一块,然后如何对这个东西做一个分割。
// 使用数组的 join() 方法将一个字符串数组转成了一个字符串,同时使用空格做分割。
element.innerHTML = ['Hello', 'webpack'].join(' ');
element.className = 'content';
return element;
}
// 调用 component() 函数创建一个包含了指定内容的 div 元素,再将其添加到 body 元素的最后,以便展示效果。
document.body.appendChild(component());
这时,如果我们直接执行 npm run build
命令对项目代码进行打包,问:这个 div
元素能不能在浏览器中被看到?答案是不能。为甚莫呢?因为这里虽然创建了 component.js
文件,但是它没有在任何地方被引入,所以就不会被打包。
那如果我们希望对这个文件进行打包的话,可以在项目中引入它,比如我们在 main.js
中引入它:
然后再执行 npm run build
命令后在浏览器中打开 index.html
页面,就能看到效果啦:
下面,我们希望通过这个 div
的 content
这个类给这个 div
添加一些样式。
好,我们先在项目的 src
目录下创建 css
文件夹,再在 css
文件夹下创建 index.css
文件,文件内容如下:
.content {
color: red;
}
我们先来思考一下,当前情况下,这个 index.css
文件到时候会被打包吗?显然,不会。因为它不在依赖关系图中。
那为了让这个文件也被打包,我们需要对它进行一个引入。可以在入口文件中引入它,但考虑到这个文件是和 component.js
关联的,我们在 component.js
中对它进行引入:
引入之后就意味着到时候这个 index.css
文件会和其它被引入的 js
文件一起,被打包到配置文件中指定的生成文件的输出位置(这里是 ./build/bundle.js
)。
那当前的话能不能进行打包呢?我们执行 npm run build
命令看下:
可以看到,运行报错了,大意是:在加载 ./src/css/index.css
文件时,模块解析失败了,你可能需要一个合适的 loader 来处理这种文件类型,当前没有配置任何 loader
来处理这个文件。再来看我们的配置文件(wk.config.js
),里面没有配置任何 loader
,所以才会报错。
上面的错误信息告诉我们需要一个 loader
来加载这个 css
文件,但是 loader
是什么呢?
loader
可以用于对模块的源代码进行转换;- 可以将这个
css
文件也看成是一个模块,我们是通过import
来加载这个模块的; - 在加载这个模块时,
webpack
其实并不知道如何对其进行加载,我们必须指定对应的loader
来完成这个功能(对模块的内容进行编译打包,我们把这个过程称为转换);
你可能会问,之前 js
怎么知道处理呢?这是因为 js
的处理过程是 webpack
已经内置过的(但是它其实内置的也不是特别好,所以我们后面还会讲到 babel-loader
,它可以对 js
再做进一步的处理)。
那么我们需要一个什么样的 loader
呢?
- 对于加载
css
文件来说,我们需要一个可以读取css
文件的loader
; - 这个
loader
最常用的是css-loader
;
css-loader
的安装(因为是开发时依赖,所以安装时使用 -D
即可):
npm install css-loader --save-dev
上面的命令也可以简写为:
npm i css-loader -D
好,下面我们执行 npm i css-loader -D
命令,在当前项目中安装 css-loader
,安装完成后就意味着当前项目中的 node_modules
文件夹下已经有 css-loader
了,然后我们就可以通过这个 css-loader
来处理 css
文件了。
那么,现在我们直接执行 npm run build
命令,webpack
是不是就能正常处理了呢?当然不是,不信你看:
还是报了之前的错误。这是为甚莫呢?因为你并没有告诉 webpack
当前项目中的 css
文件要被 css-loader
处理,所以虽然已经安装了 css-loader
,但是它和 css
文件还没有关联起来。那么怎么样把 css-loader
和 css
文件关联起来呢?
使用 loader
使用 loader
来加载 css
文件,有 3
种方式:
- 内联方式;
CLI
方式(webpack5
中不再使用);- 配置方式(推荐);
注意:虽然 webpack5
的官方文档中早前还列出了 CLI
方式(事实上,虽然 webpack
已经更新到了 5
版本,但其官方文档还没有做实时的更新,有些内容可能还是 v4
版本的),但因为该方式中需要的 --module-bind
参数(webpack4
中存在)在 webpack5
中已经没有了(通过在命令行终端运行 webpack --help=verbose
命令查看所有支持的命令和选项列表,也不难发现 webpack5
中已经不存在 --module-bind
参数了),所以这种 CLI
方式在 webpack5
中已经不支持了(实践的结果是会报错:error: unknown option '--module-bind'
)。当然,我们大可不必纠结这个东西,因为开发中通常不会使用这种 CLI
方式,而是会用配置方式,因为它通俗易懂也方便管理。
-
内联方式:内联方式使用较少,因为不方便管理;
- 在引入的样式前加上使用的
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
:必须有一个loader
属性,对应的值是一个字符串;options
:可选的属性,值是一个字符串或者对象,值会被传入到loader
中;query
:目前已经使用options
来替代;
- 传递字符串(如:
use: [ 'style-loader' ]
)是loader
属性的简写方式(如:use: [ { loader: 'style-loader'} ]
);
loader
属性:Rule.use: [ { loader } ]
的简写。
- 在配置文件(通常为
下面我们分别使用内联方式和配置方式来实践一下:
内联方式
首先确定已经安装了 css-loader
(当然,我们这里刚才已经安装过了),安装好以后就意味着项目中已经有这个模块了,那现在该如何指定它来处理 css
文件呢?
我们打开引入了 index.css
文件的 component.js
文件,将
import '../css/index.css';
这行代码为修改为:
import 'css-loader!../css/index.css';
修改后的内容就表示到时候在加载 ../css/index.css
这个文件时,使用 css-loader
来帮忙进行处理。
补充:如果后面还需要使用另外一个 loader
,比如说 style-loader
,那么继续在前面添加即可:
import 'style-loader!css-loader!../css/index.css';
好,那我们这里只添加 css-loader
进行处理,下面我们执行 npm run build
命令看下效果:
可以看到,没有报错信息了,打包成功。我们再去浏览器中看下效果:
咦~样式没生效啊,别急哈,要让样式生效还需要另外一个东西,但可以确定的是,当前 css-loader
已经对 css
文件正常处理过了,因为如果它不知道怎么处理,肯定是会报错的。而现在没有报错,说明已经正常处理了。至于页面里的样式并没有显示出来的原因,则是当前编写的样式并没有被插入到页面中,待会还需要通过另外一个 loader
将其插入到页面中,然后样式才会生效。
配置方式
我们已经知道,配置文件 webpack.config.js
(我们这里已改名为 wk.config.js
)中导出的是一个对象,那对象里面的话是可以添加很多的属性的。那配置 loader
呢就需要添加 module
属性,module
属性的值也是一个对象,既然是对象,里面又可以放键值对啦,那我们这里需要放一个键为“rules
”,值为一个数组的键值对。基本结构如下:
好,那 rules
数组里放什么呢?嗯,我们放的是 rule
对象。rule
对象中可以设置多个属性,我们这里先讲最常用的几个属性。
先上示例代码:
module: {
rules: [
{
// 规则使用正则表达式
test: /\.css$/, // 匹配资源
loader: 'css-loader'
}
]
}
下面对上面代码中的 test
和 loader
属性分别做解释。
首先是 test
属性,用来匹配资源,就是说那些能被 test
属性对应的规则匹配到的资源待会就可以被 loader
属性中指定的 loader
做处理了。那这个规则怎么写呢?我们使用正则表达式来编写规则,也就是说最终匹配资源文件时是通过正则表达式来匹配的。那这里的正则表达式 /\.css$/
的意思是匹配以 .css
结尾的文件(正则表达式中的 .
是特殊字符,所以这里要匹配 .
的话需要在前面添加 \
进行转义,而 $
字符表示匹配以前面的东西结尾的内容)。
其次是 loader
属性,其值可以是一个字符串,即要指定的 loader
的名字(实际上,loader
属性是另外一种写法的简写,这个我们后面再讲)。
所以上面这一条 rule
的意思就是:当匹配到 css
文件时,就使用 css-loader
对它(css
文件)进行处理。
但是因为 loader
属性是另外一种写法的简写,而且开发中我们其实很少用到这个 loader
属性,所以接下来我们对上面的示例代码进行修改:
module: {
rules: [
{
// 规则使用正则表达式
test: /\.css$/, // 匹配资源
use: [
{ loader: 'css-loader' }
]
}
]
}
我们用 use
属性替换掉了 loader
属性,use
属性对应一个数组,里面可以放多个 UseEntry
对象,我们这里先放一个对象,在该对象中再对 loader
属性进行设置后,其实就等价于前一个示例的写法了,但这次的写法是完整版,更常用哦~
而关于 UseEntry
对象中的 options
参数,我们现在只需要知道有这么个属性即可,后面讲自定义 loader
时我们再细讲。
其实还有一种指定 loader
的写法,就是直接在 use
属性对应的数组中传递字符串,如:
module: {
rules: [
{
// 规则使用正则表达式
test: /\.css$/, // 匹配资源
use: [
'css-loader'
]
}
]
}
需要说明的是,传递字符串(如:use: [ 'style-loader' ]
)是 loader
属性的简写方式(如:use: [ { loader: 'style-loader'} ]
)。前面 Rule
对象的 loader
属性也是 Rule.use: [ { loader } ]
的简写。
所以呢,以下三种写法是等价的:
loader: 'css-loader'
use: [
{ loader: 'css-loader' }
]
use: [
'css-loader'
]
第 1
种和第 3
种写法都是第 2
种写法的简写形式。当只需要指定一个 loader
时,可以采用第 1
种写法,因为写起来更简单;而当需要指定多个 loader
同时除了 loader
没有其它选项需要传递(即不需要传 options
参数)时,可以采用第 3
种写法,因为写起来更简单。
好,那我们这里使用第 3
种方式(use
数组中传递字符串)来进行配置,配置文件 wk.config.js
中的完整代码如下:
const path = require('path')
module.exports = {
entry: "./src/main.js",
output: {
filename: "bundle.js",
// 必须是一个绝对路径
path: path.resolve(__dirname, './build')
},
module: {
rules: [
{
// 规则使用正则表达式
test: /\.css$/, // 匹配资源
use: [
// { loader: 'css-loader' }
'css-loader'
],
// loader: 'css-loader'
}
]
}
}
然后回到 component.js
文件中,将 import 'css-loader!../css/index.css';
这行代码修改为 import '../css/index.css';
,再到 package.json
文件中确认一下 build
这项脚本的内容是否有问题(我们这里 "build": "webpack -c wk.config.js"
,就是没问题的)。接下来,我们来运行 npm run build
命令,看看刚才配置之后 webpack
是否能找到我们的指定的 loader
,来对有关文件进行处理。运行结果如下:
可见,除了一个警告没有任何报错,说明 index.css
文件已经被 css-loader
正常处理了。