前言
我们都知道,loader的本质其实就是个函数;然后,额,就不知道了;“但求精,而非浮于表面”,这是我上次和面试官面面相觑了三分钟之后的最大心理感受,也是我那次面试最大的收获,本文来自于此;
话不多说,进入正题吧;
目标
阅读本文期待你能收获什么
- loader的类别有哪些,执行顺序是怎样的(应用层面)
- webpack是如何做到的(原理层面)
正文
| loader类别 | 优先级 | |
|---|---|---|
| pre | 1 | |
| inline | 2 | |
| normal | 3 | |
| post | 4 |
实现原理
## 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模式下,存在三种特殊的执行模式,通过前置位的特殊符号进行区分
总结如下
| 前置位符号 | 效果 |
|---|---|
| !! | 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
结尾
在本文中,小结一下我们至少有两点信息
- loader的类别和执行顺序是通过优先级队列的思想实现的
- 对于内联loader我们可以采用特殊符号去决定自己要使用哪几类loader
至于【异步loader】,因篇幅有限避免混淆主题,只展示了使用层面,原理将在另一篇文章中进行讲述,有兴趣的同学欢迎一阅:webpack | loader二三事 | loader是如何执行的
感谢阅读,希望对社区有微薄贡献