上一篇介绍到的内容是刚刚进入webpack的Complier的过程,大概说明了在Complier中包含了
const isSorted = array => {}
const sortObject = (obj, keys) => {}
class Compiler {}
module.exports = Compiler;
并且大致说明了一下tapable中对于钩子的应用的基本原理。另外附上了Complier的基本结构脑图如下:
上述内容是对上一篇内容的总结,接下来继续往下研究Complier对应的编译过程。还是那个问题,要看看它对于Loader的解析过程是怎么样的。
在webpack.js中的createCompiler方法下:
const createCompiler = rawOptions => {
const options = getNormalizedWebpackOptions(rawOptions);
applyWebpackOptionsBaseDefaults(options);
const compiler = new Compiler(options.context, options); //创建Compiler对象
new NodeEnvironmentPlugin({
infrastructureLogging: options.infrastructureLogging
}).apply(compiler);
if (Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler); //调用CLIPlugin的apply方法
}
}
}
applyWebpackOptionsDefaults(options);
compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();
new WebpackOptionsApply().process(options, compiler);
compiler.hooks.initialize.call();
return compiler;
};
下图可以看到它的rules的值,就是webpack.config.js中定义的内容,如下:
使用map循环遍历,得到一个新的数组。
以CompileRule作为循环遍历的方法体进行判断,如下:
其中Object.keys(rule)的值如下:
它对应于webpack.config.js中的
//node_modules/webpack/lib/rules/RuleSetCompiler.js
compileRule(path, rule, refs) {
const unhandledProperties = new Set(
Object.keys(rule).filter(key => rule[key] !== undefined)\
//Object.keys遍历所有的属性
//filter过滤ruls的属性
//如果对应key的值不为空,则返回给Set
//最后生成一个Set对象
);
通过使用Set对象进行重构,得到unhandledProperties(直译过来是:未经处理的属性),如下:
可以通过Set的keys方法来看它包含的条目,如下:
紧接着调用this.hooks.rule.call:
这个可能会引起困扰,因为它也被命名为 rule,可是考虑上下文环境,hooks下的rule是区别于unhandledProperties对应的rule的。hooks下的rule对应于RuleSetComplier中的构造器中对于hooks的定义,如下:
很明显,它是tapable中的SyncHook,也就是一个同步钩子。其中参数包括 path、rule、unhandledProperties、compiledRule和references。
调用钩子,执行call的时候,会出现如下匿名函数,这个在上一篇tapable的介绍中有相关的说明:
(function anonymous(path, rule, unhandledProperties, compiledRule, references
) {
"use strict";
var _context;
var _x = this._x;
var _fn0 = _x[0];
_fn0(path, rule, unhandledProperties, compiledRule, references);
var _fn1 = _x[1];
_fn1(path, rule, unhandledProperties, compiledRule, references);
var _fn2 = _x[2];
_fn2(path, rule, unhandledProperties, compiledRule, references);
var _fn3 = _x[3];
_fn3(path, rule, unhandledProperties, compiledRule, references);
var _fn4 = _x[4];
_fn4(path, rule, unhandledProperties, compiledRule, references);
var _fn5 = _x[5];
_fn5(path, rule, unhandledProperties, compiledRule, references);
var _fn6 = _x[6];
_fn6(path, rule, unhandledProperties, compiledRule, references);
var _fn7 = _x[7];
_fn7(path, rule, unhandledProperties, compiledRule, references);
var _fn8 = _x[8];
_fn8(path, rule, unhandledProperties, compiledRule, references);
var _fn9 = _x[9];
_fn9(path, rule, unhandledProperties, compiledRule, references);
var _fn10 = _x[10];
_fn10(path, rule, unhandledProperties, compiledRule, references);
var _fn11 = _x[11];
_fn11(path, rule, unhandledProperties, compiledRule, references);
var _fn12 = _x[12];
_fn12(path, rule, unhandledProperties, compiledRule, references);
var _fn13 = _x[13];
_fn13(path, rule, unhandledProperties, compiledRule, references);
var _fn14 = _x[14];
_fn14(path, rule, unhandledProperties, compiledRule, references);
var _fn15 = _x[15];
_fn15(path, rule, unhandledProperties, compiledRule, references);
var _fn16 = _x[16];
_fn16(path, rule, unhandledProperties, compiledRule, references);
var _fn17 = _x[17];
_fn17(path, rule, unhandledProperties, compiledRule, references);
var _fn18 = _x[18];
_fn18(path, rule, unhandledProperties, compiledRule, references);
var _fn19 = _x[19];
_fn19(path, rule, unhandledProperties, compiledRule, references);
var _fn20 = _x[20];
_fn20(path, rule, unhandledProperties, compiledRule, references);
var _fn21 = _x[21];
_fn21(path, rule, unhandledProperties, compiledRule, references);
})
其中对应_fn0的内容如下:
(path, rule, unhandledProperties, result) => {
if (unhandledProperties.has(this.ruleProperty)) {
unhandledProperties.delete(this.ruleProperty);
const value = rule[this.ruleProperty];
const condition = ruleSetCompiler.compileCondition(
`${path}.${this.ruleProperty}`,
value
);
const fn = condition.fn;
result.conditions.push({
property: this.dataProperty,
matchWhenEmpty: this.invert
? !condition.matchWhenEmpty
: condition.matchWhenEmpty,
fn: this.invert ? v => !fn(v) : fn
});
}
}
再往下会发现,它对应如下内容:
到这一步这个流程就和在构造过程中对于钩子的注册串起来了。
这其中的this.ruleProperty的ruleProperty来自于哪儿?
/**
* @param {RuleSetCompiler} ruleSetCompiler the rule set compiler
* @returns {void}
*/
apply(ruleSetCompiler) {
ruleSetCompiler.hooks.rule.tap(
"BasicMatcherRulePlugin",
(path, rule, unhandledProperties, result) => {
//如果未经处理的属性中包含 this.ruleProperty
if (unhandledProperties.has(this.ruleProperty)) {
//在unhandleProperties的Set集合中删掉这个 ruleProperty
unhandledProperties.delete(this.ruleProperty);
const value = rule[this.ruleProperty]; //获取rule中对应于this.ruleProperty的值,对于test来说就是 /\\.css$/
//调用compileCondition
//当前对于webpack.config.js中的配置来说,第一个参数:"ruleSet[1].rules[0].test",第二个参数是: /\\.css$/
//在compileCondition中找到对应于正则表达式的分支判断,并得到对应的返回结果
const condition = ruleSetCompiler.compileCondition(
`${path}.${this.ruleProperty}`,
value
);
const fn = condition.fn;//根据正则表达式得到一个对应于正则匹配的函数
//当前上下文环境对应的 this.dataProperty是resource
//this.invert是false
//machWhenEmpty在this.invert为false的情况下取冒号后面的内容,也就是上面condition返回的matchWhenEmpty
//fn对应于fn
result.conditions.push({
property: this.dataProperty,
matchWhenEmpty: this.invert
? !condition.matchWhenEmpty
: condition.matchWhenEmpty,
fn: this.invert ? v => !fn(v) : fn
});
}
}
);
}
//node_modules/webpack/lib/rules/RuleSetCompiler.js
//RuleSetCompiler
compileCondition(path, condition) {
...
if (condition instanceof RegExp) {
return {
matchWhenEmpty: condition.test(""),
fn: v => typeof v === "string" && condition.test(v)
};
}
}
这一过程完成后 this.hooks.rule.call中每个参数的对应结果如下:
这一系列完成之后回到 node_modules/webpack/lib/rules/RuleSetCompiler.js
下的 compile:如下:
其中rules对于的结果为:
当调用return对象中的exec的属性的情况下,才会执行相应的规则。
到这一步才算是根据webpack.confg.js中的module.rules下的Loader规则生成了对应的ruleSet,也就是下图标注的位置:
既然在ruleSet中定义了exec,那就必然有调用,ruleSet.exec,调用的位置在
当resource文件找到index.css的部分的时候,会执行上述规则,调用exec并且返回响应的effects,如下:
然后再useLoaders中添加对应的返回的effects内容:
然后useLoaders传递给NormalModuleFactory下的resolveRequestArray处理:
下一篇讲Loader的编译和bundle.js的输出。