实际上,使用webpack只能打包js文件,但是我们经常会在工程中会在其他资源,如HTML、图片、CSS字体等等,我们需要将其转化为js才能进行打包,正是通过loader来实现的。
1.loader的概述
- loader本质上都是函数。loader的本质可以用公式表示:output = loader(input)
- loader可以是链式的,对一种资源设置多个loader之后,第一个loader的输入就是文件源码,之后所有的loader的输入都为上一个loader的输出,最后一个loader则直接被送到webpack进行处理
2.loader的配置
2.1 loader的引入
如果我们在js文件中引入css,由于webpack不认识css语言,因此打包的时候会报错。因此我们需要通过npm安装loader(loader都是一些第三方npm模块)
module.exports = {
//...
module:{
rules: [{
test: /\.css$/,
use: ['css-loader']
}]
}
}
与loader相关的配置都在module对象中,其中module.rules代表了模块的处理规则。规则内部有很多配置项,这里只使用了最重要的两项--test和use
- test可以接收一个正则表达式或者一个元素为正则表达式的数组,只有正则匹配上的模块才会使用这条规则
- use可以接收一个数组,数组包含该规则所使用的loader。在只有一个loader的情况下可以简化为字符串‘css-loader’
但是css-loader仅仅是处理了CSS的各种加载语法(@import和url()函数等等),要让样式起作用需要style-loader来把样式插入页面,这两个loader也是常常一起使用的
module.exports = {
//...
module:{
rules: [{
test: /\.css$/,
use: ['style-loader','css-loader']
}]
}
}
我们把style-loader加到css-loader前面,因为webpack打包是按照数组从后往前的顺序将资源交个loader的,因此要将最后生效的放在前面
2.2 loader options
loader作为预处理器,通常会给开发者提供一些配置项,在引入loader的时候可以通过options将他们传入
module.exports = {
//...
module:{
rules: [{
test: /\.css$/,
use: ['style-loader',
{
loader:'css-loader,
options:{
//css-loader配置项
}
}
]
}]
}
}
有些loader可能会使用query来代替options,从功能上来说他们没有太大区别,具体要参考loader本身的文档。
2.3 exclude与include
exclude和include也接收正则表达式或者字符串(文件绝对路径),以及由他们组成的数组。
exclude的含义是,所有被正则匹配到的模块都被排除在该规则之外,exclude: /node_modules/表示node_module中的模块不会执行这条规则。该配置项通常是必加的,否则可能拖慢整体打包速度。
include代表该规则只对正则匹配的模块生效。
exclude和include同时存在时,exclude的优先级更高
module.exports = {
//...
module:{
rules: [{
test: /\.css$/,
use: ['style-loader','css-loader'],
exclude:/src\/lib/,
include:/src/
}]
}
}
上面的代码表示对src文件夹下面的除了lib文件夹下面的模块使用css-loader和style-loader
2.4 resource和issuer
resource和issuer可以更精确地确定规则的作用范围。在webpack中,我们认为被加载的模块是resource,而加载者是issuer。
前面提到的test、exclude、include本质上属于对resource也就是被加载者的配置,如果想要对issuer加载者也增加条件限制,则需要额外写一些配置
module.exports = {
//...
module:{
rules: [{
test: /\.css$/,
use: ['style-loader','css-loader'],
exclude:/node_module/,
issuer:{
test: /\.js$/,
include: /src/pages/
}
}]
}
}
可以看到,我们添加了issuer配置对象,只有src/pages目录下面的JS文件引用CSS文件时,这条规则才会生效。
上面的代码虽然实现了我们的要求,但是text、exclude、include这些配置项有不同层级,可读性较差。
module.exports = {
//...
module:{
rules: [{
use: ['style-loader','css-loader'],
resource:{
test: /\.css$/,
exclude:/node_module/,
},
issuer:{
test: /\.js$/,
include: /src/pages/
}
}]
}
}
通过添加resource将外层的配置包起来,区分了resource和issuer中的规则,这样就一目了然了。
2.5 enforce
enforce用来指定一个loader的种类,只接收‘pre’和‘post’两种字符串类型。webpack中loader的执行顺讯分别pre、inline、normal、post四种类型。我们直接定义的loader都属于normal类型,inline形式官方已经不推荐使用,而pre和post则需要enforce来指定。
module.exports = {
//...
module:{
rules: [{
test: /\.js$/,
use: ['eslint-loader'],
enforce:'pre'
}]
}
}
可以看到,在配置中添加eslint-loader来对源码进行质量检测,其enforce的值为pre,代表它将在所有正常loader之前执行,这样可以保证其检测的代码不是被其他loader更改过。类似地,如果一个loader在所有loader之后执行,可以指定enforce值为post。
我们也可以不使用enforce,只要保证写loader的时候顺序正确即可。但是实际工程中规模越来越大,难以保证loader按照预想方式工作,使用enforce可以强制指定loader作用顺序
3. 常见loader介绍
3.1 babel-loader
babel-loader用来处理ES6+并将其编译为ES5,它使得我们可以使用最新的语言特性。
使用babel=loader需要我们通过npm安装babel-loader,@babel/core,@babel/preset-env
- babel-loader:他是使babel和webpack协同工作的模块
- @babel/core:他是babel编译器的核心模块
- @babel/preset-env:他是babel官方推荐的预置器,可根据用户设置的目标环境自动添加所需的插件和补丁来编译ES6+代码
rules:[
test: /\.js$/,
exclude:/node_module/,
use:{
loader: 'babel-loader',
option: {
cacheDirectory: true,
presets: [{
'env',
{
modules:false
}
}]
}
}
]
- babel-loader是用来处理JS文件的,需要在exclude中排除node_module,否则会编译其中所有模块,大大拖慢打包速度,并且可能改变第三方原有模块
- 对于babel-loader我们添加cacheDirectory配置项,他会启用缓存机制,在重复打包为改变过的模块是防止二次编译,同样也会加快打包速度。cacheDirectory可以接收一个字符串类型的路径也作为缓存路径,这个值也可以是true,此时缓存目录会指向node_module/.cache/babel-loader
- 由于@babel/preset-env会将ES6 Module转化为CommonJs的形式,这会导致Webpack中的tree-shaking特性消失、将@babel/preset-env的modules配置项设置为false会禁用模块语句的转化,而将ES6 Module(导入和导出语法)交给webpack本身处理
- babel-loader支持从.babelrc文件读取Babel配置,因此可以将preset和plugins从webpack配置文件中提取出来,也能达到相同的效果
3.2 ts-loader
ts-loader的作用是将ts转化为js
module.exports = {
//...
module:{
rules: [{
test: /\.ts$/,
use: ['ts-loader'],
}]
}
}
需要注意的是,ts-loader本身的配置不在ts-loader中,而是工程目录下的tsconfig.json中
{
"compilerOptions": {
"target": "es5",
"sourceMap": true
}
}
3.3 html-loader
html-loader用于将HTML文件转化为字符串并进行格式化,这使得我们可以将一个HTML片段通过JS加载进来
rules: [{
test: /\.html$/,
use: ['html-loader'],
}]
使用实例:
3.4 file-loader
file-loader用于打包文件类型的资源,并将其存储在publicPath中
module.exports = {
entry:'./app.js',
output:{
path:path.join(__dirname,'dist'),
filename:'bundle.js'
},
module:{
rules: [{
test: /\.(png|jpg|gif)$/,
use: ['file-loader'],
}]
}
}
现在对png、jpg和gif这类图片资源使用file-loader,然后就可以在JS中加载图片了
在webpack入门之二--资源输入和输出 - 掘金 (juejin.cn)之前介绍过,output.path是资源打包的输出路径,output.publicPath是资源引用路径。像上面这样使用webpack打包之后,dist目录会生成名为c6f482ac9a1905e1d7d22caa903971fc.jpg图片文件。由于没有加上output.publicPath,这里打印出来的图片路径只是文件名,默认为文件的hash值加上文件后缀。
添加上publicPath
module.exports = {
entry:'./app.js',
output:{
path:path.join(__dirname,'dist'),
filename:'bundle.js',
publicPath:'./assets/'
},
module:{
rules: [{
test: /\.(png|jpg|gif)$/,
use: ['file-loader'],
}]
}
}
此时图片路径就变成了./assets/c6f482ac9a1905e1d7d22caa903971fc.jpg
file-loader也支持配置文件文件名以及publicPath(这里的publicPath会覆盖原有的output.publicPath,通过loader的options传入)
module:{
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader:'file-loader',
options:{
name:'[name].[ext]',
publicPath: './another-path/'
}
}
}]
}
3.5 url-loader
url-loader和file-loader作用类似,唯一不同之处在于用户可以设置一个文件大小的阈值,当大于该阈值时与file-loader一样将打包的文件返回publicPath,而小于该阈值时则返回文件的base64编码格式,这样请求图片时就不需要再发送网络请求了
rules: [{
test: /\.(png|jpg|gif)$/,
use: {
loader:'url-loader',
options:{
limit:10240,
name:'[name].[ext]',
publicPath: './assets-path/'
}
}
}]
3.6 vue-loader
vue-loader用来处理vue组件,还可以将组件的模板、JS以及样式进行拆分。在安装时,除了必要的vue和vueloader以外,还要安装vue-template-compiler来编译Vue模板以及css-loader来处理样式
rules: [{
test: /\.vue$/,
use: ['vue-loader'],
}]