Webpack——loader的运行过程loader是用来预处理的,本文使用流程图分析loader的运行过程,并自定义简
🕵️♂️前面两篇分析了webpack的编译结果和编译过程,可以看出,webpack做的事情,仅仅是分析出各种模块的依赖关系,然后形成资源列表,最终打包生成到指定的文件中。 🌟而更多的功能需要借助webpack loaders和webpack plugins完成。
先看一下官网对loader的解释:Loaders | webpack
loader 用于对模块的源代码进行转换。loader 可以使你在
import
或 "load(加载)" 模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的得力方式。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中import
CSS 文件!
上面一段话我的理解是,loader是用来预处理的
下面进入正文
webpack loader
loader本质上是一个函数,它的作用是将某个源码字符串转换成另一个源码字符串返回。
loader函数的将在模块解析的过程中被调用,以得到最终的源码。
上篇文章中有webpack编译的全流程,其中有chunk中解析模块的流程
事实上,chunk中解析模块还有更详细的流程:
上图可以看到,loaders数组运行是从下往上的(从后往前),loader一次运行,经过loader处理过返回的代码交给上一个,最后交给抽象语法树。
module.exports = {
module: { //针对模块的配置,例如rules、noParse(是否不要解析某个模块)
rules: [ //模块匹配规则,可以存在多个规则
{ //每个规则是一个对象
test: /\.js$/, //正则,匹配模块的路径
use: [ //匹配到后应用的规则模块(匹配到之后使用哪些loaders)
{ //其中一个规则
loader: require('...'), //loader模块(加载器)的路径
options: { //向对应loader传递的额外参数(自己写loader时才需要)
}
} // 每个加载器的使用是一个对象
]
}
]
}
}
扩展
自己写一个loader,感受一下loader的运行过程
先来个最简单的:
./src/index.js
AAA a = 1;
./loaders/my-loader.js
module.exports = function(sourceCode){
// sourceCode: AAA a = 1;
console.log("test-loader运行了")
return sourceCode.replace(/AAA/g, "var")
}
这就是一个简单的loader,可以进行匹配AAA转化为var的预处理
./webpack.config.js
const path = require('path');
module.exports = {
mode: "development",
module: {
rules: [
{
test: /index\.js$/, //匹配index.js
use: [
{
loader: path.resolve(__dirname, './loaders/my-loader.js')
}
]
},
], //模块的匹配规则
}
}
打包:
打包出来的main.js中,AAA已经替换为var
进阶版-多个loader的执行顺序
目录结构:
|—— project
|—— loaders
|—— loader1.js
|—— loader2.js
|—— ...
|——src
|—— index.js
|—— ...
|——webpack.config.js
这个示例主要是为了感受多个loader的执行顺序,所以loader只写简单的输出,例如loader1:
module.exports = function(sourceCode){
console.log("loader1");
return sourceCode;
}
webpack.config.js
module.exports = {
mode: "development",
module: {
rules: [
{
test: /index\.js$/, //正则表达式,匹配模块的路径
use: ["./loaders/loader1", "./loaders/loader2"] //匹配到了之后,使用哪些加载器
// 这里是简化的写法,字符串会被放到require里
}, //规则1
{
test: /\.js$/, //正则表达式,匹配模块的路径
use: ["./loaders/loader3", "./loaders/loader4"] //匹配到了之后,使用哪些加载器
} //规则2
], //模块的匹配规则
}
}
打包:
可以看到,在rules中,两个规则均能匹配到index.js
,顺序是从loader1到loader4。而打包的输出是相反的:
开始时,loader数组为空数组[]
。在匹配时,先匹配到了规则1,加入loader数组:["loader1", "loader2"]
,之后规则2也匹配到,两个数组合并为["loader1", "loader2","loader3", "loader4"]
,这些loader加载出来都是一个函数。
使用这些loader时,是从后往前的,文件内容先交给loader4,loader4返回的内容交给loader3...
所以输出的顺序为4-3-2-1。
那么,如果index.js有依赖关系,loader的运行顺序怎么判断呢?
在src中新增一个a.js
文件,并在index.js
引入a.js
// ./src/index.js
require("./a");
打包:
过程分析:
参考文章最开始的chunk解析流程图,入口模块为
index.js
,先运行入口模块进行loader,使用index.js
与规则进行匹配,都能匹配到,于是像上个示例一样先输出loader4-loader3-loader2-loader1
接下来进行抽象语法树分析、记录依赖的a.js
,替换依赖函数,处理依赖(即为处理a.js
模块)
然后读取a.js
的文件内容,处理loaders,使用a.js与规则进行匹配,只能匹配到["loader3", "loader4"]
,从后往前运行,输出loader4-loader3
所以最终输出顺序为4-3-2-1-4-3
注:以上自定义loader仅用于帮助理解loader的处理过程,在实际开发中一般不会自定义loader
转载自:https://juejin.cn/post/7382223729198579746