webpack4流程分析3

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. 递归编译
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享