A loader is just a JavaScript module that exports a function.css
从语法角度看,loader是一个普通的Node.js模块,只是必须以函数格式导出来供使用。若是有必要可使用一切Node.js功能模块。vue
从功能角度看,loader是在Webpack中做用于指定格式的资源文件并将其按照必定格式转换输出。例如:less-loader将less文件转换为css文件输出。node
单一职责,一个Loader只作一件事情,正由于职责越单一,因此Loaders的组合性强,可配置性好。webpack
loader支持链式调用,上一个loader的处理结果能够传给下一个loader接着处理,上一个Loader的参数options能够传递给下一个loader,直到最后一个loader,返回Webpack所指望的JavaScript。git
在学习loader的配置时,最好搭个简易的Webpack Demo,执行webpack
命令打包,能够验证一下配置是否有误。github
loader在Webpack中的配置有多种写法,下面一一详解。web
先来看一个简单的loader配置。正则表达式
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
},
],
},
}
复制代码
loader是配置在module.rules中,module.rules的含义是建立模块的规则,module.rules的值是一个数组,其中每一项都是一项规则。loader是用来生成符合Webpack的模块的。而后Webpack把这些模块打包起来生成对应的js文件。loader是在打包前执行的。vue-cli
以下图所示,这是用style-loader
和css-loader
两个loader生成的模块。 npm
在这里称module.rules中每一项规则为Rule,下面来说配置规则Rule的条件和配置规则Rule的loader。
在简单的loader配置中,test:/\.css$/
,是筛选到名称以.css
结尾的文件后,交给user
选项里面的loader处理一下。
那么test
选项的做用就是筛选资源,符合条件的资源让这项规则中的loader处理。
test
的值能够是字符串、正则表达式、函数、数组。
const path = require('path');
module.exports = {
module: {
rules: [
{
test: path.resolve(__dirname, 'src/css'),
//test: path.resolve(__dirname, 'src/css/index.css'),
use: ['style-loader','css-loader']
},
],
},
}
复制代码
user
选项里面的loader处理一下。\project\03personal\05Webpack_demo\src\css\index.css
复制代码
module.exports = {
module: {
rules: [
{
test: function (path) {
return path.indexOf('.css') > -1
},
use: ['style-loader','css-loader']
},
],
},
}
复制代码
user
选项里面的loader处理一下。const path = require('path');
module.exports = {
module: {
rules: [
{
test: [/\.css$/,path.resolve(__dirname, 'src/css')]
use: ['style-loader','css-loader']
},
],
},
}
复制代码
符合条件的资源让这项规则中的loader处理,用法和Rule.test同样。
const path = require('path');
module.exports = {
module: {
rules: [
{
include:/\.css$/,
//include: path.resolve(__dirname, 'src/css'),
//include: path.resolve(__dirname, 'src/css/index.css'),
//include: [/\.css$/,path.resolve(__dirname, 'src/css')],
//include:function (content) {
//return content.indexOf('src/css') > -1
//},
use: ['style-loader','css-loader']
},
],
},
}
复制代码
符合条件的资源要排除在外,不能让这项规则中的loader处理,用法和Rule.test同样。例如排除node_modules中的css文件。
const path = require('path');
module.exports = {
module: {
rules: [
{
exclude:/node_modules/,
//exclude: path.resolve(__dirname, 'node_modules'),
//exclude: [/node_modules/ , path.resolve(__dirname, 'node_modules')],
//exclude:function (content) {
//return content.indexOf('node_modules') > -1
//},
use: ['style-loader','css-loader']
},
],
},
}
复制代码
用法和Rule.test同样,可是要注意是匹配引入资源的文件路径,
如在main.js中引入css/index.css
const path = require('path');
module.exports = {
module: {
rules: [
{
issuer: /\main\.js$/,
//issuer: path.resolve(__dirname, 'main.js'),
//issuer: [/\main\.js$/ , path.resolve(__dirname, 'main.js')],
//issuer:function (content) {
//return content.indexOf('main.js') > -1
//},
use: ['style-loader', 'css-loader']
},
],
},
}
复制代码
Rule.issuer 和 Rule.test、Rule.include 、Rule.exclude同时使用时候,也是“与”的关系。
此选项也可筛选资源,符合条件的资源让这项规则中的loader处理。
但配置resource
选项后,test
、include
、exclude
选项不能使用。issuer
选项不生效。
resource
选项中有如下子选项
test
选项,用法和Rule.test同样。exclude
选项,用法和Rule.exclude同样。include
选项,用法和Rule.include同样。not
选项,值为数组,数组每一项能够为字符串、正则表达式、函数,只要符合数组中任一项条件的资源就不能交给user
选项里面的loader处理一下。and
选项,值为数组,数组每一项能够为字符串、正则表达式、函数,必须符合数组中每一项条件的资源才能交给user
选项里面的loader处理一下。or
选项,值为数组,数组每一项能够为字符串、正则表达式、函数,只要符合数组中任一项条件的资源就能够交给user
选项里面的loader处理一下。const path = require('path');
module.exports = {
module: {
rules: [
{
resource:{
test:/\.css$/,
include: path.resolve(__dirname, 'src/css'),
exclude: path.resolve(__dirname, 'node_modules'),
},
use: ['style-loader', 'css-loader']
},
],
},
}
复制代码
匹配资源引入路径上从问号开始的部分。例
import './ass/main.css?inline'
复制代码
上面代码中Rule.resourceQuery要匹配?inline
,例
const path = require('path');
module.exports = {
module: {
rules: [
{
resourceQuery:/inline/,
// resourceQuery:function (content) {
//return content.indexOf('inline') > -1
// },
//resourceQuery:[/inline/],
use: ['style-loader', 'css-loader']
},
],
},
}
复制代码
在上面已经提到过Rule.use的用法。意思是使用哪些loader处理符合条件的资源。
use: ['style-loader']
实际上是use: [ { loader: 'style-loader'} ]
的简写。
还能够经过options
传入loader,能够理解为loader的选项。
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
]
},
],
},
}
复制代码
use
的值还能够是函数,返回一个数组,参数为info,info中有如下内容
compiler
:当前webpack的编译器(能够是undefined值)。issuer
:引入被处理资源的所在文件的绝对路径。realResource
:被处理资源的绝对路径。resource
:被处理资源的绝对路径,它经常与realResource替代,只有当资源名称被请求字符串中的!=!覆盖时才不近似。resourceQuery
:被处理资源的绝对路径中?后面的部分。module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: (info) =>{
console.log(info)
return [
'style-loader',
{
"loader": 'css-loader',
},
]
},
},
],
},
}
复制代码
参数info打印以下图所示
module.exports = {
module: {
rules: [
{
test: /\.css$/,
loader: 'css-loader',
},
],
},
}
复制代码
loader: 'css-loader'
是 use: [ { loader: 'css-loader'} ]
的简写。
当规则匹配时,只使用第一个匹配规则。
例如说要处理css文件资源时,one.css要用url-loader处理,two.css要用file-loader处理。能够用Rule.oneOf来配置,其用法和module.rules同样。
module.exports = {
module: {
rules: [
{
test: /\.css$/,
oneOf: [
{
resourceQuery: /one/, // one.css?one
//test: /one\.css/,
use: 'url-loader'
},
{
resourceQuery: /two/, // two.css?two
//test: /one\.css/,
use: 'file-loader'
}
]
},
],
},
}
复制代码
从右到左,从下到上执行。换句话来讲,就是后写的先执行,跟栈同样后进先出。
rules: [
{
test: /\.less$/,
use: ['style-loader','css-loader','less-loader']
},
],
复制代码
以上配置中,less-loader先执行,再执行css-loader,最后执行style-loader。
rules: [
{
test: /\.less$/,
use:[
{
loader:'style-loader'
},
{
loader:'css-loader'
},
{
loader:'less-loader'
}
]
},
],
复制代码
以上配置中,less-loader先执行,再执行css-loader,最后执行style-loader。
rules: [
{
test: /\.less$/,
loader:'style-loader',
},
{
test: /\.less$/,
loader:'css-loader',
},
{
test:/\.less$/,
loader:'less-loader'
}
],
复制代码
以上配置中,less-loader先执行,再执行css-loader,最后执行style-loader。
由以上三个例子,能够得知,在同一个规则Rule的条件下,其规则Rule中的loader都是后写的先执行。从空间上来看,就是从右到左,从下到上执行。
用Rule.enforce来控制,其有两个值:
pre
:优先执行post
:最后执行rules: [
{
test:/\.less$/,
loader:'less-loader'
},
{
test: /\.less$/,
loader:'css-loader',
},
{
test: /\.less$/,
loader:'style-loader',
},
],
复制代码
若是按上面的书写顺序,style-loader先执行,再执行css-loader,最后执行less-loader。结果确定会报错。能够用Rule.enforce来控制loader的执行顺序。既不改变loader的书写顺序,也能够正确执行。
rules: [
{
test:/\.less$/,
loader:'less-loader',
enforce:'pre'
},
{
test: /\.less$/,
loader:'css-loader',
},
{
test: /\.less$/,
loader:'style-loader',
enforce:'post'
},
],
复制代码
此时,less-loader先执行,再执行css-loader,最后执行style-loader。
其实loader还有一种“内联”的用法。例
import 'style-loader!css-loader!less-loader!./index.css';
复制代码
使用 ! 将资源中的 loader 分开。分开的每一个部分都相对于当前目录解析。
在这里能够把loader分为四种
其执行顺序 pre -> normal -> inline ->post
尽量使用 module.rules,由于这样能够减小源码中的代码量,而且能够在出错时,更快地调试和定位 loader 中的问题。
Webpack官网中不推荐你们使用“内联”loader,因此在讲loader的执行顺序时把inline类型的loader排除掉了。
在Vue Cli3中配置loader,有两种方法,一是经过configureWebpack
选项来配置,二是经过chainWebpack
选项来配置。
在配置中,可使用vue-cli-service inspect
来审查一个 Vue CLI 项目的 webpack config。
在项目中package.json文件中scripts
中添加一条命令
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"inspect": "vue-cli-service inspect --mode production > output.js"
},
复制代码
inspect
这条命令的意思是把这个项目的生产环境下的解析好的 webpack 配置输出到output.js这个文件中。
若是是--mode development
,就是开发环境下的webpack config。
configureWebpack
选项的值能够是对象,也能够是函数
值为对象。
最后经过webpack-merge合并到最终的配置中。也就是说在这里,只能新增loader配置,不能修改loader配置或者删除lodaer配置。
例如在vue.config.js中配置
module.exports = {
configureWebpack:{
module:{
rules:[
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
}
]
}
},
}
复制代码
执行npm run inspect
后,在output.js中会发现,以下图所示
值为函数。
函数接收config做为参数,参数内容是webpack 配置,此时能够经过config参数来修改webpack的配置,也能够返回一个对象来经过webpack-merge合并到最终的配置中。
例如在vue.config.js中配置
module.exports = {
configureWebpack:config =>{
config.module.rules[10]={
test:/\.less$/,
use:['style-loader','css-loader','less-loader']
}
},
}
复制代码
执行npm run inspect
后,在output.js中会发现,以下图所示,原先处理.less文件的loader配置已经被替成后面修改的。
可是用这种方法去修改loader的配置,太粗放了,若是要进行更细粒度的修改loader配置,可使用chainWebpack
来配置。
chainWebpack
选项的值是一个函数,会接收一个基于webpack-chain 的 ChainableConfig 实例。采用链式写法来配置Webpack。 用法文档点这里we。
这里只讲关于loader配置的新增、修改、删除的用法。
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
},
}
复制代码
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
//添加test选项
.test(/\.less$/)
//添加include选项,其值是数组
.include.add('/src/').add('/view/').end()
//添加exclude选项,其值是数组
.exclude.add('/node_modules/').end()
//添加issuer选项
.issuer('/\main\.js$/')
//添加resourceQuery选项
.resourceQuery('/inline/')
},
}
复制代码
执行npm run inspect
后,在output.js中会发现,以下图所示,就是上面配置生成的。
也可使用Rule.resource来配置规则的条件,在chainWebpack中这样配置:
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
.issuer('/\main\.js$/')
.resourceQuery('/inline/')
//添加resource选项
.resource({
test:/\.less$/,
include:['/src/','/view/'],
exclude:['/node_modules/'],
})
},
}
复制代码
执行npm run inspect
后,在output.js中会发现,以下图所示,就是上面配置生成的。
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
.test(/\.less$/)
//先建立一个具名的use,后面修改有用到这个名称
.use('styleloader')
//往这个具名的use中添加一个loader
.loader('style-loader')
//添加多个loader时要先.end()回到主链路
.end()
.use('cssloader')
.loader('css-loader')
.end()
.use('lessloader')
.loader('less-loader')
},
}
复制代码
执行npm run inspect
后,在output.js中会发现,以下图所示,就是上面配置生成的。注意书写顺序,最后写的先执行。
例如要给less-loader添加参数。
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
.test(/\.less$/)
.use('lessloader')
.loader('less-loader')
.options({
// 这里配置全局变量
globalVars: {
'primary': '#fff'
}
})
},
}
复制代码
.options()
的参数是个对象,在对象里面配置loader的参数。
执行npm run inspect
后,在output.js中会发现,以下图所示,就是上面配置生成的。
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
.use('lessloader')
.tap(options =>{
options.globalVars.primary= 'red';
return options
})
},
}
复制代码
用.tag()
来实现,其参数是个函数,函数的参数是原loader的参数对象集合options,经过修改参数options,再返回options达到修改规则Rule的loader的参数的目的。
修改前
npm run inspect
后,在
output.js中会发现,以下图所示,就是上面修改后生成的。
修改前
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
.use('lessloader')
.loader('sass-loader')
},
}
复制代码
修改后
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
.uses.clear()
.end()
.use('styleloader')
.loader('style-loader')
}
}
复制代码
修改后
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
.test(/\.less$/)
.oneOf('vue-modules')
.resourceQuery('/module/')
.use('css-loader')
.loader('css-loader')
.end()
.use('less-loader')
.loader('less-loader')
.end()
.end()
.oneOf('src')
.resourceQuery('/src/')
.use('style-loader')
.loader('style-loader')
.end()
.use('css-loader')
.loader('css-loader')
.end()
.use('less-loader')
.loader('less-loader')
}
}
复制代码
执行npm run inspect
后,在output.js中会发现,以下图所示,就是上面配置生成的。
以前建立Rule.oneOf规则组,咱们给每一个Rule.oneOf都起了名称,能够利用.oneOf(name)
找这个Rule.oneOf修改,修改和建立的语法同样。
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule')
.oneOf('vue-modules')
.resourceQuery('/module11/')
.use('css-loader')
.loader('sass-loader')
}
}
复制代码
执行npm run inspect
后,在output.js中会发现,修改后的结果以下图所示。
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule1')
.test(/\.less$/)
.use('lessloader')
.loader('less-loader')
config.module
.rule('myRule2')
.test(/\.less$/)
.use('styleloader')
.loader('style-loader')
config.module
.rule('myRule3')
.test(/\.less$/)
.use('cssloader')
.loader('css-loader')
}
}
复制代码
执行npm run inspect
后,在output.js中会发现,以下图所示,就是上面配置生成的。
由于在同一个规则Rule的条件下,其规则Rule中的loader都是后写的先执行。
全部在同一规则Rule的条件test(/\.less$/)
下,先执行css-loader、再执行style-loader、最后执行less-loader,这样的执行顺序确定是不对的。应该先执行less-laoder,再执行css-loader,最后执行style-loader。
这是能够利用.pre()
、.post()
、.enforce('pre'/'post')
来控制loader的执行顺序。
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule1')
.test(/\.less$/)
.use('lessloader')
.loader('less-loader')
.end()
.pre()
config.module
.rule('myRule2')
.test(/\.less$/)
.use('styleloader')
.loader('style-loader')
.end()
.post()
config.module
.rule('myRule3')
.test(/\.less$/)
.use('cssloader')
.loader('css-loader')
}
}
复制代码
执行npm run inspect
后,在output.js中会发现,以下图所示,就是上面配置生成的。
或者这样也能够实现
module.exports = {
chainWebpack: config =>{
config.module
.rule('myRule1')
.test(/\.less$/)
.use('lessloader')
.loader('less-loader')
.end()
.enforce('pre')
config.module
.rule('myRule2')
.test(/\.less$/)
.use('styleloader')
.loader('style-loader')
.end()
.enforce('post')
config.module
.rule('myRule3')
.test(/\.less$/)
.use('cssloader')
.loader('css-loader')
}
}
复制代码