Webpack之loader配置詳解

前言

1、loader是什麼

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

2、loader的特色

單一職責,一個Loader只作一件事情,正由於職責越單一,因此Loaders的組合性強,可配置性好。webpack

loader支持鏈式調用,上一個loader的處理結果能夠傳給下一個loader接着處理,上一個Loader的參數options能夠傳遞給下一個loader,直到最後一個loader,返回Webpack所指望的JavaScript。git

3、loader的配置

在學習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-loadercss-loader兩個loader生成的模塊。 npm

在這裏稱module.rules中每一項規則爲Rule,下面來說配置規則Rule的條件和配置規則Rule的loader。

一、配置規則Rule的條件

Rule.test

在簡單的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']
            },
        ],
    },
}
複製代碼
  • 值爲函數時,接收的參數爲資源的絕對路徑。返回true表示該資源能夠交給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']
            },
        ],
    },
}
複製代碼

Rule.include

符合條件的資源讓這項規則中的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']
            },
        ],
    },
}
複製代碼

Rule.exclude

符合條件的資源要排除在外,不能讓這項規則中的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.issuer

用法和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同時使用時候,也是「與」的關係。

Rule.resource

此選項也可篩選資源,符合條件的資源讓這項規則中的loader處理。

但配置resource選項後,testincludeexclude選項不能使用。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']
            },
        ],
    },
}
複製代碼

Rule.resourceQuery

匹配資源引入路徑上從問號開始的部分。例

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.test、Rule.include、Rule.exclude、Rule.issuer、Rule.resourceQuery同時使用時候,是「與」的關係,必須同時符合以上全部配置的條件纔可讓這項規則中的loader處理。
  • Rule.issuer、Rule.resourceQuery、Rule.resource同時使用時候,也是「與」的關係。必須同時符合以上全部配置的條件纔可讓這項規則中的loader處理。

二、配置規則Rule的loader

Rule.use

在上面已經提到過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打印以下圖所示

Rule.loader

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                loader: 'css-loader',
            },
        ],
    },
}
複製代碼

loader: 'css-loader'use: [ { loader: 'css-loader'} ]的簡寫。

Rule.oneOf

當規則匹配時,只使用第一個匹配規則。

例如說要處理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'
                    }
                ]
            },
        ],
    },
}
複製代碼

4、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都是後寫的先執行。從空間上來看,就是從右到左,從下到上執行。

5、控制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

其執行順序 pre -> normal -> inline ->post

儘量使用 module.rules,由於這樣能夠減小源碼中的代碼量,而且能夠在出錯時,更快地調試和定位 loader 中的問題。

Webpack官網中不推薦你們使用「內聯」loader,因此在講loader的執行順序時把inline類型的loader排除掉了。

6、在Vue Cli3中配置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配置

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配置

chainWebpack選項的值是一個函數,會接收一個基於webpack-chain 的 ChainableConfig 實例。採用鏈式寫法來配置Webpack。 用法文檔點這裏we

這裏只講關於loader配置的新增、修改、刪除的用法。

新增一個規則Rule

module.exports = {
    chainWebpack: config =>{
        config.module
            .rule('myRule')
    },
}
複製代碼

添加規則Rule的條件

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中會發現,以下圖所示,就是上面配置生成的。

添加規則Rule的loader

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中會發現,以下圖所示,就是上面配置生成的。注意書寫順序,最後寫的先執行。

添加規則Rule的loader的參數

例如要給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中會發現,以下圖所示,就是上面配置生成的。

修改規則Rule的loader的參數

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中會發現,以下圖所示,就是上面修改後生成的。

修改規則Rule的loader

修改前

有兩種作法

  • 修改其中一個loader
module.exports = {
    chainWebpack: config =>{
        config.module
            .rule('myRule')
            .use('lessloader')
                .loader('sass-loader')
    },
}
複製代碼

修改後

  • 將這個Rule的loader所有清除從新添加
module.exports = {
    chainWebpack: config =>{
        config.module
            .rule('myRule')
            .uses.clear()
                .end()
            .use('styleloader')
                .loader('style-loader')
    }
}
複製代碼

修改後

建立Rule.oneOf規則組

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規則組,咱們給每一個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中會發現,修改後的結果以下圖所示。

控制loader的執行順序

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中會發現,以下圖所示,就是上面配置生成的。

此時loader的執行順序就是先執行less-laoder,再執行css-loader,最後執行style-loader。

或者這樣也能夠實現

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

相關文章
相關標籤/搜索