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')
}
}
複製代碼