webpack | loader二三事 | loader类别

940 阅读1分钟

前言

我们都知道,loader的本质其实就是个函数;然后,额,就不知道了;“但求精,而非浮于表面”,这是我上次和面试官面面相觑了三分钟之后的最大心理感受,也是我那次面试最大的收获,本文来自于此;

话不多说,进入正题吧;

目标

阅读本文期待你能收获什么

  • loader的类别有哪些,执行顺序是怎样的(应用层面)
  • webpack是如何做到的(原理层面)

正文

loader类别优先级
pre1
inline2
normal3
post4
实现原理
## webpack.config.js
rules : [
    {
        enforce: 'pre',
        test: /\.js$/,
        use: ["pre-loader","pre-loader2"]
    },
    {
        test: /\.js$/,
        use: ["normal-loader","normal-loader2"]
    },
    {
        enforce: 'post',
        test: /\.js$/,
        use: ["post-loader","post-loader2"]
    }
]
## webpack内部处理机制  其实就是优先级队列  先排序,再整合
let preLoaders = [];
let postLoaders = [];
let normalLoaders = [];
for (let index = 0; index < rules.length; index++) {
    const rule = rules[index];
    if(rule.test.test(resource)){
        if(rule.enforce == 'pre'){
            preLoaders.push(...rule.use);
        }
        else if(rule.enforce == 'post'){
            postLoaders.push(...rule.use);
        }
        else {
            normalLoaders.push(...rule.use);
        }
    }
}
...
// 获得最终的loader
let loaders = [
    ...postLoaders,
    ...inlineLoaders,
    ...normalLoaders,
    ...preLoaders,
]

特殊符号决定内联loader中要执行的loader类别

在webpack中存在三种引用loader的方式:配置文件、内联(import(loader1!loader2!index.js))、脚本指定,在inline模式下,存在三种特殊的执行模式,通过前置位的特殊符号进行区分

image-20201222135436202

总结如下

前置位符号效果
!!noPrePostAutoLoaders 不要前置、后置和普通loader 只要内联loade
-!noPreAutoLoaders 不要前置和普通loader
!noAutoLoaders 不要普通loader
实现原理
## index.js
let request = "inline-loader!inline-loader2!./index.js";

## webpack执行逻辑
let inlineLoaders = request.replace(/^-?!+/,"").replace(/!!+/g,'!').split('!');
...
// noPrePostAutoLoaders 不要前置、后置和普通loader 只要内联loader
if(request.startsWith('!!')){
    loaders = inlineLoaders;
}
// noPreAutoLoaders  不要前置和普通loader
else if(request.startsWith('-!')){
    loaders = [
        ...postLoaders,
        ...inlineLoaders
    ]
}
// noAutoLoaders  不要普通loader
else if(request.startsWith('!')){
    loaders = [
        ...postLoaders,
        ...inlineLoaders,
        ...preLoaders
    ]
}
// 全要
else {
    loaders = [
        ...postLoaders,
        ...inlineLoaders,
        ...normalLoaders,
        ...preLoaders,
    ]
}
使用案例
"inline-loader!inline-loader2!./index.js"

异步loader

是指loader在执行时,其上下文对象会有一个async方法,调用时会中断webpack工作流,返回一个回调函数callback,只有用户主动调用这个callback时,webpack才会继续向下执行流程;

使用案例
index.js
!!async-loader!async-loader2!./index.js
async-loader.js
function loader(source) {
    let callback = this.async();
    console.log(new Date());
    setTimeout(()=>{
     callback(
         null,
         source + "//async1"
     )
    },3000)
}
loader.pitch = function (){
    console.log('pitch async-loader1');
}
module.exports = loader
async-loader2.js
function loader(source) {
   let callback = this.async();
   console.log(new Date());
   setTimeout(()=>{
    callback(
        null,
        source + "//async2"
    )
   },3000)
}
loader.pitch = function (){
    console.log('pitch async-loader2');
}
module.exports = loader

image-20201217141255859

结尾

在本文中,小结一下我们至少有两点信息

  • loader的类别和执行顺序是通过优先级队列的思想实现的
  • 对于内联loader我们可以采用特殊符号去决定自己要使用哪几类loader

至于【异步loader】,因篇幅有限避免混淆主题,只展示了使用层面,原理将在另一篇文章中进行讲述,有兴趣的同学欢迎一阅:webpack | loader二三事 | loader是如何执行的

感谢阅读,希望对社区有微薄贡献