一个Web工程通常会包含HTML、JS、CSS、图片、字体等多种类型的静态资源,且这些资源之间都存在着某种联系。对于Webpack来说,所有这些静态资源都是模块,开发者可以像加载一个JS文件一样去加载它们。比如在index.js中加载style.css。
// index.js
import "./style.css"
提示
对于初学者,可能会有这样的疑问:从JS中加载CSS文件具有怎样的意义呢?从结果来看,其实和之前并没有什么差别,这个style.css可以被打包并生成在输出资源目录下,对index.js文件也不会产生实质性的影响。这句引用的实际意义是描述了JS文件与CSS文件之间的依赖关系。
loader概述
loader是Webpack中的一个核心概念,可以理解为一个代码转换的工具。每个loader本质上都是一个函数,可以表示为以下形式:out = loader(input)
这里的input可能是工程源文件的字符串,也可能是上一个loader转化后的结果,output则包括了转化后的代码、sourcemap和AST对象。如果这是最后一个loader,结果将直接被送到Webpack进行后续处理,否则将作为下一个loader的输入向后传递。loader可以是链式的,开发者可以对一种资源设置多个loader,第一个loader的输入是文件源码,之后所有loader的输入都为上一个loader的输出。用公式表达则为:output = loaderA(loaderB(loaderC(input)))
在Webpack中,本身只认识Javascript,对于其他类型的资源,开发者必须先定义一个或多个loader对其进行转译,输出为Webpack能够接收的形式再继续进行,因此loader做的实际上是一个预处理的工作。
loader的引入
// app.js
import "./style.css"
// style.css
body {
text-align: center;
padding: 100px;
color: #fff;
background-color: #09c;
}
此时工程中还没有任何loader,如果直接打包会看到报错提示。这是因为Webpack无法处理CSS语法。
如果想让Webpack能够处理CSS语法,需要在工程中引入css-loader。
提示
loader都是一些第三方npm模块,因此使用loader的第一步就是先从npm安装它。
在工程目录下安装css-loader。
npm install css-loader -D
接下来将loader引入工程中,具体配置如下
// webpack.config.js
module.exports = {
// ...
module: {
rules: [{
test: /\.css$/,
use: ['css-loader'],
}]
}
}
与loader相关的配置都在module对象中,其中module.rule代表了模块的处理规则。每条队则内部可以包含很多配置项,这里我们只使用了最重要的两项:test和use。
- test可接收一个正则表达式或者一个元素为正则表达式的数组,只有正则匹配上的模块才会使用这条规则。
- use可接收一个数组,数组包含该规则所使用的loader。在只有一个loader时可以将其简化为字符串“css-loader”。
此时再进行打包,就不会出现报错的情况了。但是CSS的样式没有在页面上生效。这是因为css-loader的作用仅仅是处理CSS的各种加载语法(@import和url()函数等),如果要使样式起作用需要style-loader来把样式插入页面。css-loader与style-loader通常是配合在一起使用的。
提示
Webpack在打包时是按照数组从后往前的顺序将资源交给loader处理的。所以在使用多个loader时,要把最后生效的放在前面。例如在使用style-loader和css-loader时,要把style-loader加到css-loader前面。
loader更多配置
exclude与include
exclude与include用于排除或包含指定目录下的模块,可接收正则表达式或者字符串(文件绝对路径),或者由它们组成的数组。
// webpack.config.js
module.exports = {
...
module: {
rules:[{
test: /\.css$/,
use: ['style-loader','css-loader'],
exclude: /node_modules/,
}]
}
}
上面exclude的含义是,所有被正则匹配到的模块都排除在该规则之外,也就是说,node_modules中的模块不会执行这条规则。该配置项通常是必加的,否则将拖慢整体的打包速度。
与exclude相反,include的含义是只对正则匹配到的模块生效。
温馨提示
当exclude和include同时存在时,exclude的优先级更高。
resource与issuer
resource与issuer用于更加精准地确定模块规则的作用范围。 resource指的是被加载模块,issuer指的是加载者。
请看下面的例子
// index.js
import "./style.css"
在示例中,被加载模块(resource)是style.css,加载者(issuer)是index.js。
test、exclude、include本质上均属于对resource也就是被加载者的配置,如果想要对issuer加载者也增加条件限制,则要额外写一些配置。比如,只想让/src/pages目录下的JS可以引用CSS。
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.css$/,
use: ['style-loader','style-loader'],
exclude: /node_modules/,
issuer: {
test: /\.js$/,
include: /src/pages/,
},
}]
}
}
可以看到,添加了issuer配置对象,其形式与之前对resource条件的配置并无太大差异,但只有/src/pages/目录下面的JS文件引用CSS文件,这条规则才会生效。
上面的配置虽然实现了的需求,但是test、exclude、include这些配置项分布于不同的层级上,可读性较差。事实上还可以将它改为另一种等价的形式。
// webpack.config.js
module.exports = {
...
module: {
rules: [{
use: ['style-loader','css-loader'],
resource: {
test: /\.css$/,
exclude: /node_modules/,
},
issuer: {
test: /\.js$/,
exclude: /node_modules/,
}
}]
}
}
上面的配置,分别包裹了resource和issuer中的规则,这样就一目了然了。
enforce
enforce用来指定一个loader的种类,只接收pre或post两种字符串类型的值。
Webpack中的loader按照执行顺序可分为pre、inline、normal、post四种类型。上面的示例中直接定义的loader都属于normal类型,inline形式官方已经不推荐使用,而pre和post则需要使用enforce来指定。
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.js$/,
enforce: 'pre',
use: 'eslint-loader',
}]
}
}
在上面的配置中,添加了一个eslint-loader来对源码进行质量检测,其enforce的值为pre。表示它将在所有正常loader之前执行,这样可以保证其检测的代码不是被其他loader更改过的。类似地,如果某一个loader需要在所有loader之后执行,可以指定其enforce为post。
温馨提示
事实上,开发者可以不使用enforce而只要保证loader顺序是正确的即可。配置enforce的主要目的是使模块规则更加清晰,可读性更强。在实际工程中,配置文件可能会出现达到上百行的情况,此时开发者很难保证各个loader都按照预想的方式工作,而使用enforce可以强制指定loader的作用顺序。
示例代码仓库
https://gitee.com/zero_79152105/webpack-vblog
原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/webpack-loader.html