1. NormalModule
class NormalModule extends Module {
constructor({ type, request, rawRequest, loaders, resource, parser, generator }) {
// 这些参数都是如何来的? 到NormalModuleFactory中查看
this.type = type
this.request = request // 经过loader处理过的 loaders.map()
this.rawRequest = rawRequest
this.loaders = loaders // loader
this.resource = resource // code
this.parser = parser // js解析器
this.generator = generator // 模版生成
}
// 调用build方法开始构建
build(options, compilation, resolver, fs, callback) {
return this.doBuild(options, compilation, resolver, fs, (err) => {
// webpack中使用acorn 我们可以使用babel来模拟
// 关注主流程 parser 过程比较复杂先跳过不分析
const result = this.parser.parse(
this._ast || this._source.source(),
{},
cb
);
callback();
});
}
doBuild() {
// 处理loader 这里就是loader执行的时机 将code经过loader处理之后在给parse解析
// runLoaders的流程也相对独立 跳过
runLoaders({}, (err, result) => {
return callback();
});
}
}
2. NormalModuleFactory
class NormalModuleFactory {
constructor(context, resolverFactory, options) {
this.resolverFactory = resolverFactory;
this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
let resolver = this.hooks.resolver.call(null);
resolver(result, (err, data) => {
// 我们要清楚NormalModule中的参数就要分析resolver这个hook
let createdModule = new NormalModule(result);
});
});
// 这一步找文件和loader的绝对路径 使用的是增强的node中require找文件规则
this.hooks.resolver.tap("NormalModuleFactory", () => (data, callback) => {
// 参数就这里返回的 主要是返回loader和文件 module的绝对路径和一些其他的构建信息
// 获取解析器 loader的解析器
const loaderResolver = this.getResolver("loader");
// 文件和模块
const normalResolver = this.getResolver("normal", data.resolveOptions);
// inline loader的解析
// loader排序 在runLoaders中按照顺序执行post inline normal pre
callback(null, {
// 返回数据给NormalModule
context,
request: loaders.map(),
dependencies: data.dependencies,
userRequest,
rawRequest: request,
loaders,
resoutce,
parser: this.getParser("javascript/auto"),
generator: this.getGenerator("javascript/auto"),
});
});
}
// create方法创建模块
create() {
// 执行factory 这部分的逻辑之前分析过了
const factory = this.hooks.factory.call(null);
factory(result, (err, module) => {
callback(null, module);
});
}
// resolverFactory是compiler中
getResolver(type, resolveOptions) {
return this.resolverFactory.get(type, resolveOptions || {});
}
getParser() {
return this.createParser(type, parserOptions);
}
getGenerator(type, generatorOptions) {
return this.createGenerator(type, generatorOptions);
}
createParser(type, parserOptions) {
// 针对不同的模块创建不同的解析器 这些钩子是哪里的
const parser = this.hooks.createParser.for(type).call(parserOptions);
this.hooks.parser.for(type).call(parser, parserOptions);
return parser;
}
createGenerator(type, generatorOptions) {
const generator = this.hooks.createGenerator
.for(type)
.call(generatorOptions);
this.hooks.generator.for(type).call(generator, generatorOptions);
return generator;
}
}
3.ResolverFactory
// 在compiler中
this.resolverFactory = new ResolverFactory();
class ResolverFactory extends Tapable {
// get方法获取不同的loader处理
get(type, resolveOptions) {
const newResolver = this._create(type, resolveOptions);
return newResolver;
}
_create(type, resolveOptions) {
// type对应 webpackOptionsApply 中 tap 的类型 normal loader
resolveOptions = this.hooks.resolveOptions.for(type).call(resolveOptions);
// 创建解析器 enhanced-resolve 增强的路径解析包
// todo 不分析这里
const resolver = Factory.createResolver(resolveOptions);
return resolver;
}
}
4.WebpackOptionsApply
// 在执行webpack的时候会执行这个文件 之前我们 EntryOptionPlugin()就是这里挂载的 还有很多其他的
// js的解析器和模板生成的api都是JavascriptModulesPlugin中挂载的
new JavascriptModulesPlugin().apply(compiler); // js模块
// new JsonModulesPlugin().apply(compiler); // json模块
// moduleIds
// chunkIds
// 为Factory.createResolver提供默认的参数配置 ResolverFactory中触发钩子
// compiler.resolverFactory.hooks.resolveOptions.for("normal").tap();
// compiler.resolverFactory.hooks.resolveOptions.for("loader").tap();
5. JavascriptModulesPlugin
class JavascriptModulesPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"JavascriptModulesPlugin",
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.createParser
.for("javascript/auto")
.tap("JavascriptModulesPlugin", (options) => {
// webpack中的Parser使用的acorn 这部分逻辑比较复杂 先跳过
return new Parser(options, "auto");
});
normalModuleFactory.hooks.createGenerator
.for("javascript/auto")
.tap("JavascriptModulesPlugin", () => {
return new JavascriptGenerator();
});
}
);
}
}
6.Compilation
class Compilation extends Tapable {
_addModuleChain(context, dependency, onModule, callback) {
moduleFactory.create({}, (err, module) => {
// 如果有依赖项就要处理模块的依赖项
const afterBuild = () => {
// 处理模块的依赖 那么我们如何找到模块的依赖
// 得到ast语法树遍历找到依赖收集
this.processModuleDependencies(module, (err) => callback(null, module));
};
// 构建模块 调用module的build方法 就是 module.build(
this.buildModule(module, false, null, null, (err) => {
afterBuild();
});
});
}
// webpack非常恶心的地方就是各种回调 搞死人
buildModule(module, optional, origin, dependencies, thisCallback) {
module.build({}, (err) => {
return thisCallback();
});
}
// 模块build之后会处理依赖 这些依赖是哪里来的? 生成ast之后分析 require和import得到
processModuleDependencies(module, callback) {
module.dependencies.forEach((item, callback) => {
// 拿到对应的工厂
const factory = item.factory;
// 工厂创建模块 然后build 递归处理
factory.crete({}, (err, dependentModule) => {
this.buildModule();
});
});
}
}
7.总结
在我们执行module.build的时候
=> runLoaders 会先处理loader
=> 然后会使用解析器生成ast做分析
我们本次主要是分析 module 中的一些参数
1. loader和文件的怎么找到的 我们使用了enhanced-resolve路径解析包找到资源的绝对路径
2. js parse哪来的? JavascriptModulesPlugin中会tap对应的钩子 而执行webpack函数的时候会apply()
3. 在 ModuleFactory 的 resolver 中我们会返回创建 module 的参数(最要是找路径和构建需要的一些参数)
4. 接下来我们就要分析parse的过程 生成ast 找到依赖项 递归处理
// 简述模块的打包流程
1. 找到模块和loader的绝对路径 normalFacroty 中的 resolver 流程
2. 读取内容经过loader的处理 得到 js 模块 Module中的 runLoaders过程
3. 将js通过parse得到ast语法树
4. 分析ast中的依赖项(require, import) 分析模块的依赖
5. 递归编译
发表评论
还没有评论,快来抢沙发吧!