写在前面
这是webpack实战
系列笔记的第5篇记录,前几篇记录如下:
- 打包第一个应用
- 模块化与模块打包
- 资源输入与输出
- 一切皆模块
上一篇简单描述了一切皆模块的思想,学以致用,来实践下~
1. 关于 loader
每个loader本质上都是一个函数,可用公式表达其本质:
output = loader(input)
- input可能是工程源文件的字符串,也可能是上一个loader转化后的结果,包含:
- 字符串(转化后的结果)
- source map
- AST对象(抽象语法树)
- output同样包含上述几种信息。
如果是最后一个loader,其结果直接被送到webpack中做后续处理;否则作为下一个loader的input向后传递
。
举个例子
当使用 babel-loader 将 ES6+ 代码转化为 ES5 时,带入公式:
ES5 = babel-loader(ES6+)
上述描述说过,loader本身是一个函数那么loader是如何工作的呢?
module.exports = function loader (content, map, meta) {
var callback = this.async();
var result = handler(content, map, meta);
callback(
null, // error
result.content, // 转换后的内容
result.map, // 转换source-map
result.meta // 转换后的AST
)
}
可以看出,该函数对接受到的内容进行转换,然后返回转换后的结果。
2. loader配置
那在应用层面应该如何实施呢?
在一切皆模块
中说过静态资源的类型是各式各样的,比如静态HTML/CSS/JS、图片字体音视频等,webpack如何处理这各类资源呢?ok,loader的应用场景来了。
loader,字面意思是装载器,但在webpack中实际用途则是预处理器:webpack本身只认识JavaScript,对于其他类型的资源必须先定义一个或多个loader对其进行转译,输出为webpack能够接收的形式再继续进行,因此loader做的实际上是一个预处理的工作。
2.1 引入
那loader到底应该如何使用呢?举例要在js中引入css文件:
// index.js
import './assets/common/css'
/* common.css */
body{
width: 100vw;
height: 100vh;
text-align: center;
background: gray;
}
ok,现在我们执行打包操作,build一下,会发现在终端报错:
为解决报错,我们需要用到的就是loader了~
可以在上图报错内容除看到,提示我们没找到合适的loader来处理,并且给出css-loader
提示,我们按提示安装:
// npm
npm install css-loader
// 或者 yarn 与npm二选一即可
yarn add css-loader
安装完成后,仍需在webpack.config.js中进行loader配置:
const path = require('path')
module.exports = {
entry: {
index: './src/index.js',
},
output: {
filename: '[name].js'
},
mode: 'development',
// loader配置
module: {
rules: [
// css
{
test: /\.css$/,
use: ['css-loader']
}
]
}
}
可以看到,对loader进行配置,配置项都在module对象中的rules模块。rules模块是一个数组,代表了要对模块进行处理的规则。在此处,我们使用到的规则有test
和use
:
- test:接收一个正则表达式或者一个元素为正则表达式的数组,只有正则匹配上的模块才会使用本条规则;
- use:接收一个数组,代表该规则所使用的loader。
然后进行打包操作:
// npm
npm run build
// 或者 yarn 与npm二选一即可
yarn build
然后发现,打包错误解决。
但,新的问题出现了:
此时我们在浏览器打开index.html
,发现样式并没有生效。原因是css-loader 的作用是处理css的加载语法而不是做style的样式渲染,因此我们需要添加一个 style-loader 来进行样式渲染。
2.2 链式loader
在上面我们说了需要在引入一个 style-loader 来进行样式渲染处理,先安装:
// npm
npm install style-loader
// 或者 yarn 与npm二选一即可
yarn add style-loader
接着搭配之前的webpack配置,做一些修改:
module.exports = {
...
// loader配置
module: {
rules: [
// css style
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}
可以看到,我们将style-loader
写在了use
的数组中,并且细心的朋友可能发现写在了css-loader
之前,这就是链式loader。
那么为什么要区分顺序呢?在前面描述关于loader的公式中我们介绍过:
output = loader(input) ,
在链式webpack打包中,是按照数组从后往前的顺序将资源交给loader去处理,因此最后生效的应该放在前面。
此时,我们在执行打包操作,然后可以在浏览器中看到index.html
页面加载了样式:
2.3 其他配置
2.3.1 options配置
有些loader会有专门的配置项,形式上可能会有一些不同,如:
module.exports = {
...
// loader配置
module: {
rules: [
// css
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
// css-loader相关配置
...
}
}
]
}
]
}
}
当然,具体配置项参数和值则需要参考相应loader的文档来进行配置,用时参阅该loader文档即可。
2.3.2 exclude 和 include
从字面意思理解,这两个分别是用来排除或者包含
指定目录下模块的。
如:
module.exports = {
...
// loader配置
module: {
rules: [
// css
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
exclude: /node_modules/
}
]
}
}
上面置顶了exclude: /node_modules/
,则代表着该目录下的所有模块都不会被此条规则限制,也就是说node_modules
中的模块不会执行该规则。
同样,include用途与此类似:
module.exports = {
...
// loader配置
module: {
rules: [
// css
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
include: /src/
}
]
}
}
可以看到此处使用的是include
,代表该规则只对正则匹配到的模块生效,也就是说只对src下的模块生效。
在exclude
和include
同时存在的情况下,exclude
优先级更高!
通常情况下,在使用loader时,需要配置它,以此来加速打包速度,不配置的话打包会将所有模块打包,可能拖慢整体的打包速度。
2.3.3 resource 与 issuer
有时候,我们会在项目中看到关于resource
和issuer
的相关配置,那么这两个配置是做什么的呢?
其实与exclude
和include
类似,都是用于规定模块作用范围的配置。但是区别是exclude
和include
对规则的作用范围更加的精确。
如:
// index.js
import './src/common.css'
在webpack中,我们认为被加载模块是resource,加载者是issuer,在上述代码中,css作为被加载者,而index作为加载者。
那么具体如何使用呢?
module.exports = {
...
// loader配置
module: {
rules: [
// css
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
exclude: /node_modules/,
issuer: {
test: /\.js$/,
include: /src/pages/
}
}
]
}
}
在上面我们看到配置了issuer
对象,限制是让src/pages
目录下js可以引用使用规则css-loader
。
我们看完后会发现一个风格问题: 代码可读性较差。可以稍加改善:
module.exports = {
...
// loader配置
module: {
rules: [
// css
{
use: ['style-loader', 'css-loader'],
resource: {
test: /\.css$/,
exclude: /node_modules/
},
issuer: {
test: /\.js$/,
exclude: /node_modules/
}
}
]
}
}
通过添加resource对象来讲外层配置包裹起来,区分resource和issuer的规则,看上去即可一目了然,但实际本质一样。可选择一种风格进行配置。
小结
本篇介绍了loader的作用和意义,以及在项目中实际使用时的一些配置,如引入、使用过程、链式loader、loader的配置等,从各大小方面均能做到对项目有优化或效率提升。
下一篇介绍几个项目常用loader以及如何自定义loader。