NormalModule
// 经过上面的分析 我们知道调用 NormalModule 的build方法是真正的开始
// NormalModule 继承 Module
class NormalModule extends Module {
build() {
// doBuild就是runLoaders过程 先不考虑loader的情况
return this.doBuild(options, compilation, resolver, fs, (err) => {
// 使用acorn解析生成ast 然后遍历
const result = this.parser.parse(
// this._source = this.createSource()
// return new OriginalSource(source, this.request);
this._ast || this._source.source(),
{},
cb
);
});
}
}
// Module 继承 DependenciesBlock
class Module extends DependenciesBlock {}
//里面有个重要的方法
class DependenciesBlock {
constructor() {
this.dependencies = [];
this.blocks = [];
this.variables = [];
}
addDependency(dependency) {
this.dependencies.push(dependency);
}
}
NormalModuleFactory
// 在上一章 我们知道 NormalModule 是在 NormalModuleFactory 中执行factory的时候new的
// 在 resolver 的过程中返回了 new module 的参数 我们先看下参数
// 暂时先不考虑loader的过程 这个步骤找到了绝对路径
callback(null, {
context,
// request, // request: loaders.map(),
request: path.posix.join(context, request), // 绝对路径
dependencies: data.dependencies, // 依赖 这个时候是 SingleEntryDependency
userRequest: path.posix.join(context, request), // 绝对路径 暂时使用path
rawRequest: request, // './src/index.js'
loaders: [],
// 这里暂时使用entry的绝对路径来替代 忽略loader的部分
resource: path.posix.join(context, request),
// matchResource,
// resourceResolveData,
settings, // {type: 'javascript/auto'}
type: settings.type,
// js解析器 JavascriptModulesPlugin插件
parser: this.getParser(settings.type, settings.parser),
// 不同的模块使用不同的生成器 为模版生成提供api方法
generator: this.getGenerator(settings.type, settings.generator),
resolveOptions: data.resolveOptions,
});
JavascriptModulesPlugin
// parser 哪来的? 不同类型的文件我们会有不同的解析器
// 在执行webpack的过程中 会处理 WebpackOptionsApply
new JavascriptModulesPlugin().apply(compiler); // js模块
new JsonModulesPlugin().apply(compiler); // json模块
... 其他模块对应的插件处理
class JavascriptModulesPlugin {
apply(compiler) {
// 在compiler的hooks上有很多 compilation 的钩子
// 当创建 compilation 的时候 会触发对应的钩子 执行一些列的hook
compiler.hooks.compilation.tap(
"JavascriptModulesPlugin",
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.createParser
.for("javascript/auto") // 针对不同的type会有不同的钩子
.tap("JavascriptModulesPlugin", (options) => {
// js的解析器就是这里得到的
return new Parser(options, "auto");
});
// 模版主要和生成代码相关逻辑之后分析
compilation.mainTemplate.hooks.modules.tap();
}
);
}
}
Parser
// 在上一篇的时候 我们直接跳过了parse的过程 现在简单看下
// 我们在初始化compilation之后通过模块工厂创建模块就可以拿到这里的js解析器
class Parser extends Tapable {
constructor(options, sourceType = "auto") {
super();
// 一堆的hooks
this.hooks = {};
}
// 我们调用parser方法 source是经过loader-runner处理过的代码
// 主要是分两步 一个是parse解析 一个是遍历(触发各种hooks)
parse(source, initialState) {
// 生成ast语法树
let ast = Parser.parse(source, { sourceType: "module" });
// 和作用域相关的 例如函数作用域 块级作用域等 先不考虑
this.scope = {};
const state = (this.state = initialState || {});
// 对语法树进行分析 主要就是用 module 的 addDependency 方法增加一些依赖 还有模板的处理
if (this.hooks.program.call(ast, comments) === undefined) {
// 针对不同的节点做不同的处理
this.detectMode(ast.body);
this.prewalkStatements(ast.body);
this.blockPrewalkStatements(ast.body);
this.walkStatements(ast.body);
}
return state;
}
}
HarmonyModulesPlugin
// webpack对不同的依赖模块会有不同的模版处理
// ES module最终会给 HarmonyModulesPlugin 里面的依赖来处理
// CommonJS Module 的会给 CommonJsPlugin 里面的依赖处理 对其他的模块也有处理
// 我们以Esmodule为例分析 其他的跳过
// 1. 这些hook是什么时候注册的
还是和之前一样 我们在执行webpack的时候 会执行 WebpackOptionsApply 里面是做了很多事的
new ConstPlugin().apply(compiler);
new ImportPlugin(options.module).apply(compiler);
new HarmonyModulesPlugin(options.module).apply(compiler);
new APIPlugin().apply(compiler);
new CommonJsPlugin(options.module).apply(compiler);
// 2.HarmonyModulesPlugin主要做了些啥
// 引入一些列的 不同语法的依赖类型
const HarmonyInitDependency = require("./HarmonyInitDependency");
...
// 不同的语法在编译过程中挂载的hooks
const HarmonyDetectionParserPlugin = require("./HarmonyDetectionParserPlugin");
const HarmonyImportDependencyParserPlugin = require("./HarmonyImportDependencyParserPlugin");
const HarmonyExportDependencyParserPlugin = require("./HarmonyExportDependencyParserPlugin");
class HarmonyModulesPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"HarmonyModulesPlugin",
(compilation, { normalModuleFactory }) => {
// 设置工厂和模块
compilation.dependencyFactories.set(
HarmonyImportSpecifierDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
HarmonyImportSpecifierDependency,
new HarmonyImportSpecifierDependency.Template()
);
const handler = (parser, parserOptions) => {
// 判断 esmodule
new HarmonyDetectionParserPlugin().apply(parser);
// 处理import
new HarmonyImportDependencyParserPlugin(this.options).apply(parser);
// 处理export
new HarmonyExportDependencyParserPlugin(this.options).apply(parser);
};
// 注册parser钩子 创建新的 normalModule 时钩子会被执行
// handler会初始化各种plugin 注册相关的hooks
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("HarmonyModulesPlugin", handler);
}
);
}
}
HarmonyDetectionParserPlugin
class HarmonyDetectionParserPlugin {
apply(parser) {
parser.hooks.program.tap("HarmonyDetectionParserPlugin", (ast) => {
// const isHarmony 判断import export的声明
const module = parser.state.module;
const compatDep = new HarmonyCompatibilityDependency(module);
// 增加依赖
module.addDependency(compatDep);
const initDep = new HarmonyInitDependency(module);
module.addDependency(initDep);
});
}
}
import export
// 我们些代码 es module 一般是 import 和 export 语法
// 但是也会有多种写法 对应ast不同的称谓 之后学习babel的时候在重要学习 这里先简单有点印象
import x1 from './xx'
import {x2} from './xx'
import * as x3 from './xx'
// export也会有很多不同的写法 感觉平时主要是 默认导出和具名导出
// import中type是一样的 export的type是不一样的 之后在处理ast的时候要注意
export default {name: 'name'}
export const a = 'a'
export function b() {}
HarmonyImportDependencyParserPlugin
// 主要用来处理import的
class HarmonyImportDependencyParserPlugin {
apply(parser) {
// import的钩子
parser.hooks.import.tap(
"HarmonyImportDependencyParserPlugin",
(statement, source) => {
// 根据条件增加依赖
parser.state.module.addDependency(clearDep);
parser.state.module.addDependency(sideEffectDep);
}
);
parser.hooks.importSpecifier.tap()
parser.hooks.expression()
}
}
HarmonyExportDependencyParserPlugin
// 主要是用来处理export
class HarmonyExportDependencyParserPlugin {
apply(parser) {
// export 在parse的过程中会触发各种的hook
parser.hooks.export.tap(
"HarmonyExportDependencyParserPlugin",
(statement) => {
// new HarmonyExportHeaderDependency() 不同的依赖
parser.state.current.addDependency(dep);
}
);
parser.hooks.exportImport.tap();
parser.hooks.exportExpression.tap();
parser.hooks.exportSpecifier.tap();
parser.hooks.exportImportSpecifier.tap();
}
}
demo
// index.js 我们些一个demo 先只考虑 es module的情况 不分析 commonjs规范
// 在 https://astexplorer.net/ 中查看ast的结构
import sync from "./sync";
console.log(sync);
import(/*webpackChunkName: 'async'*/ "./async").then((result) => {
console.log(result.default);
});
// 我们得到的ast语法树 body 中有三个 我们会遍历处理这里面的节点
const obj = {
// 第一个是import
"type": "ImportDeclaration",
"specifiers": [
{
"type": "ImportDefaultSpecifier",
"local": { "type": "Identifier", "name": "sync"}
}
],
"source": { "type": "Literal", "value": "./sync", "raw": "\"./sync\""}
},
// 后面两个都是 表达式
const obj1 = {
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": { "type": "Identifier", "name": "console" },
"property": { "type": "Identifier", "name": "log" },
},
"arguments": [
{ "type": "Identifier", "name": "sync" }
]
}
}
const obj2 = {
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "ImportExpression",
"source": { "type": "Literal", "value": "./async", "raw": "\"./async\"" }
},
"property": {"type": "Identifier", "name": "then" },
},
"arguments": [
{
"type": "ArrowFunctionExpression",
"params": [ { "type": "Identifier", "name": "result" } ],
"body": { "type": "BlockStatement", "body": [ /** console和上面一样的 */ ]
}
}
]
}
}
parse
// parse的过程是直接使用了 Parser.parse 生成语法树
// 遍历的工具 babel有 @babel/traverse esprima 是有 estraverse acorn自己处理
// 上面的过程主要是经过5个步骤 我们一个一个来看
this.hooks.program.call(ast, comments)
this.detectMode(ast.body);
this.prewalkStatements(ast.body);
this.blockPrewalkStatements(ast.body);
this.walkStatements(ast.body);
1.program
// this.hooks.program.call(ast, comments)
// 会触发 HarmonyDetectionParserPlugin 中注册的钩子
// 根据条件给我们增加依赖
HarmonyCompatibilityDependency
HarmonyInitDependency
2.detectMode
// 这一步主要是处理 严格模式 scope 的逻辑跳过
if (isLiteral && statements[0].expression.value === "use strict") {
this.scope.isStrict = true;
}
3.prewalkStatements
// this.prewalkStatements(ast.body);
// 1. 遍历执行
this.prewalkStatement(statement);
// 2. 根据不同的类型执行不同的分支 我们demo中的三个 type为 ImportDeclaration 和 ExpressionStatement
// prewalkStatement不处理 ExpressionStatement 的类型 主要还是在处理 import和export
// ast.body的第一个节点会进入
function prewalkStatement(statement) {
switch (statement.type) {
case "ImportDeclaration":
this.prewalkImportDeclaration(statement);
break;
}
}
// 3.处理import prewalkImportDeclaration import 又会根据 specifiers 分不同的类型
function prewalkImportDeclaration(statement) {
for (const specifier of statement.specifiers) {
const name = specifier.local.name;
switch (specifier.type) {
case "ImportDefaultSpecifier":
// 不同的类型会触发不同的 hooks
this.hooks.importSpecifier.call(statement, source, "default", name);
break;
case "ImportSpecifier":
this.hooks.importSpecifier.call(
statement,
source,
specifier.imported.name,
name
);
break;
case "ImportNamespaceSpecifier":
this.hooks.importSpecifier.call(statement, source, null, name);
break;
}
}
}
// 4. 触发hooks执行 HarmonyImportDependencyParserPlugin 增加依赖
const clearDep = new ConstDependency("", statement.range);
const sideEffectDep = new HarmonyImportSideEffectDependency()
parser.state.module.addDependency(clearDep);
parser.state.module.addDependency(sideEffectDep);
// 5.又新增了两个依赖
HarmonyCompatibilityDependency
HarmonyInitDependency
ConstDependency
HarmonyImportSideEffectDependency
4.blockPrewalkStatements
// this.blockPrewalkStatements(ast.body);
// 1. 同理遍历执行 blockPrewalkStatement
this.blockPrewalkStatement(statement);
// 2.判断statement的type类型 很明显我们的demo没有对应的分支流程
function blockPrewalkStatement(statement) {
switch (statement.type) {
case "VariableDeclaration":
this.blockPrewalkVariableDeclaration(statement);
break;
case "ExportDefaultDeclaration":
this.blockPrewalkExportDefaultDeclaration(statement);
break;
case "ExportNamedDeclaration":
this.blockPrewalkExportNamedDeclaration(statement);
break;
case "ClassDeclaration":
this.blockPrewalkClassDeclaration(statement);
break;
}
}
5. walkStatements
// this.walkStatements(ast.body);
// 1.还是遍历执行
this.walkStatement(statement);
// 2. 也是判断type的不同类型
// 在我们的case中 body第一个节点不走这个分支 后面两个都是ExpressionStatement
function walkStatement(statement) {
switch (statement.type) {
case "ExpressionStatement":
this.walkExpressionStatement(statement);
break;
}
}
// 3. walkExpressionStatement 其实是处理expression
this.walkExpression(statement.expression);
// 4.回顾下ast的结构
// type callee arguments
// 5. 判断不同的类型 我们的demo都是CallExpression
function walkExpression(expression) {
switch (expression.type) {
case "CallExpression":
this.walkCallExpression(expression);
break;
}
}
// 6. 执行 walkCallExpression
function walkCallExpression(expression) {
// 判断 expression.callee.type 的类型
if (
expression.callee.type === "MemberExpression" &&
expression.callee.object.type === "FunctionExpression"
) {
} else if (expression.callee.type === "FunctionExpression") {
} else if (expression.callee.type === "Import") {
} else {
// 计算函数表达式
const callee = this.evaluateExpression(expression.callee);
if (callee.isIdentifier()) {
// 判断是不是标识符
const callHook = this.hooks.call.get(callee.identifier);
if (callHook !== undefined) {
// 执行callHook
let result = callHook.call(expression);
}
const callAnyHook = this.hooks.callAnyMember.get(identifier);
if (callAnyHook !== undefined) {
let result = callAnyHook.call(expression);
}
}
// 处理callee和arguments
if (expression.callee) this.walkExpression(expression.callee);
if (expression.arguments) this.walkExpressions(expression.arguments);
}
}
// 7.执行walkExpression(expression.callee)
function walkExpression(expression) {
switch (expression.type) {
case "MemberExpression":
this.walkMemberExpression(expression);
break;
}
}
// 8. walkMemberExpression
function walkMemberExpression(expression) {
const exprName = this.getNameForExpression(expression);
const expressionHook = this.hooks.expression.get(exprName.name);
const expressionAnyMemberHook = this.hooks.expressionAnyMember.get();
// 深度遍历执行object
this.walkExpression(expression.object);
}
// 9.我们的object中是Identifier
function walkExpression(expression) {
switch (expression.type) {
case "Identifier":
this.walkIdentifier(expression);
break;
}
}
// 10. 我们执行walkIdentifier
function walkIdentifier() {
// 会判断作用域
// if (!this.scope.definitions.has(expression.name)) { }
const hook = this.hooks.expression.get(); // 有就执行
const result = hook.call(expression);
}
// 11.处理完callee我们就要处理arguments 是多个 需要遍历处理
// 增加了一个依赖 HarmonyImportSpecifierDependency
// 12. 处理完ast.body中第二个元素之后 需要处理第三个元素
// 在处理callee的时候 object的type为ImportExpression
// 在处理arguments时为 ArrowFunctionExpression
// 13.处理callee
this.walkExpression(expression.object);
this.walkCallExpression(expression);
// 14. type为ImportExpression
walkCallExpression(expression) {
// type为import 执行对应的hooks 进入到 ImportParserPlugin 插件中
let result = this.hooks.importCall.call(expression);
}
acorn
// webpack的ast和我们在网站上生成的代码有一些差异
// 当webpack在使用acorn的时候 有一些默认的参数
// ecmaVersion: 11, sourceType: "module", acorn的版本也有一定的差异
// 主要看第二个节点的callee部分结构如下
Node {
type: 'CallExpression',
start: 47,
end: 64,
loc: SourceLocation {
start: Position { line: 4, column: 0 },
end: Position { line: 4, column: 17 }
},
range: [ 47, 64 ],
callee: Node {
type: 'Import',
start: 47,
end: 53,
loc: SourceLocation { start: [Position], end: [Position] },
range: [ 47, 53 ]
},
arguments: [
Node {
type: 'Literal',
start: 54,
end: 63,
loc: [SourceLocation],
range: [Array],
value: './async',
raw: '"./async"'
}
]
}
ImportParserPlugin
// 在webpackOptionsApply中
new ImportPlugin(options.module).apply(compiler)
// ImportPlugin中会引入
const ImportParserPlugin = require("./ImportParserPlugin");
const ImportDependency = require("./ImportDependency");
// 也是设置依赖和模版 然后执行handler
class ImportPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"ImportPlugin",
(compilation, { contextModuleFactory, normalModuleFactory }) => {
compilation.dependencyFactories.set(
ImportDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
ImportDependency,
new ImportDependency.Template()
);
const handler = (parser, parserOptions) => {
new ImportParserPlugin(options).apply(parser);
};
// 处理js
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("ImportPlugin", handler);
}
);
}
}
ImportParserPlugin
class ImportParserPlugin {
apply(parser) {
parser.hooks.importCall.tap("ImportParserPlugin", (expr) => {
const depBlock = new ImportDependenciesBlock();
// 增加block
parser.state.current.addBlock(depBlock);
});
}
}
dependencies
// 经过上面ast的处理 我们当前模块增加了5个依赖 对应不同的模板
// 这些依赖时做什么的 暂时还是不清楚的
HarmonyCompatibilityDependency // 用于定义 exports:__esModule
HarmonyInitDependency //
ConstDependency // clean操作 删除
HarmonyImportSideEffectDependency // 模版
HarmonyImportSpecifierDependency // 模版
blocks
// blocks属性 对应我们 动态import的模块
// ImportDependenciesBlock
发表评论
还没有评论,快来抢沙发吧!