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')
    }
}
复制代码

相关文章
相关标签/搜索