01 => 绑定entryOption钩子

  1. 内部主要实例化了SingleEntryPlugin类,并调用内部的apply方法
  2. 实例化之后有一行代码:compiler.hooks.entryOption.call(options.context, options.entry),会立即对entryOption钩子进行调用 ```javascript // 此处我们传入的是./src/index.js,为字符串类型 const itemToPlugin = (context, item, name) => { if (Array.isArray(item)) {
    1. return new MultiEntryPlugin(context, item, name);
    } return new SingleEntryPlugin(context, item, name); };

module.exports = class EntryOptionPlugin { apply(compiler) { compiler.hooks.entryOption.tap(“EntryOptionPlugin”, (context, entry) => { if (typeof entry === “string” || Array.isArray(entry)) { itemToPlugin(context, entry, “main”).apply(compiler); } else { … } return true; }); } }

  1. <a name="p1Xzz"></a>
  2. ### 02 => SingleEntryPlugin类
  3. 内部主要绑定了make钩子,compilation.addEntry是编译的入口。直到此处,compiler准备工作就完成了。
  4. ```javascript
  5. class SingleEntryPlugin {
  6. // 将传入的属性绑定到this上
  7. constructor(context, entry, name) {
  8. this.context = context;
  9. this.entry = entry;
  10. this.name = name;
  11. }
  12. // 主要绑定了make钩子,此处是编译的入口
  13. apply(compiler) {
  14. compiler.hooks.make.tapAsync(
  15. "SingleEntryPlugin",
  16. (compilation, callback) => {
  17. const { entry, name, context } = this;
  18. // dep 可以简单理解为 entry
  19. // 即compilation.addEntry(context, entry, name, callback);
  20. const dep = SingleEntryPlugin.createDependency(entry, name);
  21. compilation.addEntry(context, dep, name, callback);
  22. }
  23. );
  24. }
  25. }

03 => 调用compiler的run方法,开始对源文件编译

  1. Compiler类在webpack源码01中说讲过,当时主要是进行实例化操作,绑定了一些内部属性
  2. 当我们调用run方法时,内部会调用一系列Tapable的钩子,如beforeRun、run等等
  3. 最终执行一行代码:this.hooks.make.callAsync,执行上述绑定的make钩子
    class Compiler extends Tapable {
    // callback => 回调函数
     run(callback) {
     // finalCallback函数是专门处理错误的,此处我们不考虑错误的情况
     // 调用beforeRun钩子
       this.hooks.beforeRun.callAsync(this, err => {
         if (err) return finalCallback(err);
       // 调用run钩子
       this.hooks.run.callAsync(this, err => {
                 if (err) return finalCallback(err);
                 this.readRecords(err => {
                     if (err) return finalCallback(err);
           // 调用compile方法
           // 传入onCompiled回调,onCompiled函数作用是将处理好的chunk输出到dist目录
           // onCompiled函数后续再讲
                     this.compile(onCompiled);
                 });
             });
     })
    }
    // 创建NormalModuleFactory工厂
    // NormalModuleFactory类内部就一个create方法,等到之后再说
    createNormalModuleFactory() {
         const normalModuleFactory = new NormalModuleFactory(
             this.options.context,
             this.resolverFactory,
             this.options.module || {}
         );
     // 执行normalModuleFactory钩子
         this.hooks.normalModuleFactory.call(normalModuleFactory);
     // 返回实例化后的NormalModuleFactory类
         return normalModuleFactory;
     }
    // Compilation对象 => 每一次编译的过程都会生成一个Compilation对象
    // 实例化Compilation类并设置属性
    // 调用thisCompilation和compilation钩子
    newCompilation(params) {
         const compilation = new Compilation(this);
         compilation.fileTimestamps = this.fileTimestamps;
         compilation.contextTimestamps = this.contextTimestamps;
         compilation.name = this.name;
         compilation.records = this.records;
         compilation.compilationDependencies = params.compilationDependencies;
         this.hooks.thisCompilation.call(compilation, params);
         this.hooks.compilation.call(compilation, params);
         return compilation;
     }
    // 此处主要是normalModuleFactory,因此主要看createNormalModuleFactory函数
    newCompilationParams() {
         const params = {
             normalModuleFactory: this.createNormalModuleFactory(),
             contextModuleFactory: this.createContextModuleFactory(),
             compilationDependencies: new Set()
         };
         return params;
     }
      compile(callback) {
     // 可以理解成简单的一个对象,内部有一个create方法
       const params = this.newCompilationParams();
     // 调用beforeCompile钩子
     this.hooks.beforeCompile.callAsync(params, err => {
         if (err) return callback(err);
       // 调用compile钩子
       this.hooks.compile.call(params);
       // 创建Compilation类并触发一系列钩子
       const compilation = this.newCompilation(params);
       // 执行02中绑定的make钩子,开始编译
       this.hooks.make.callAsync(compilation, err => {
           if (err) return callback(err);
         // 编译完成后,处理的函数
         compilation.finish(err => {
             if (err) return callback(err);
           // 根据已有的module,生成对应的代码
           compilation.seal(err => {
               if (err) return callback(err);
             // 调用编译完成后的函数
             this.hooks.afterCompile.callAsync(compilation, err => {
                             if (err) return callback(err);
                             return callback(null, compilation);
                         });
           })
         })
       })
     })
    }
    }
    

04 => Compilation对象

CompilationCompiler是webpack核心的两个对象。

  • Compiler只会生成一次,记录了完成的webpack环境信息
  • Compilation在watch时会生成多次,代表编译过程
    // Compilation类也继承了Tapable库
    // 设置一些内部属性
    class Compilation extends Tapable {
      constructor(compiler) {
        super();
      this.hooks = {
          addEntry: new SyncHook(["entry", "name"]),
        succeedEntry: new SyncHook(["entry", "name", "module"]),
        finishModules: new AsyncSeriesHook(["modules"]),
        beforeChunks: new SyncHook([]),
        afterChunks: new SyncHook(["chunks"]),
        ...
      }
      this.compiler = compiler;
      this.options = options;
      this.entries = [];
      this.chunks = [];
      this.modules = [];
      ...
    }
    }
    

05 => 调用compiler.hooks.make,开始编译

调用的是Compilation对象中的addEntry方法

class Compilation extends Tapable {
  // context => 上下文对象
  // entry => option.entry,传入的入口地址
  // name => "main"
  // callback => 回调函数
    addEntry(context, entry, name, callback) {
    // 调用addEntry钩子
      this.hooks.addEntry.call(entry, name);
    ...
    // 调用_addModuleChain方法
    this._addModuleChain(
            context,
            entry,
            module => {
        // this.entries为存入所有入口模块的地址,初始为空数组
                this.entries.push(module);
            },
            (err, module) => {
        ...
        // 调用succeedEntry钩子
                this.hooks.succeedEntry.call(entry, name, module);
                return callback(null, module);
            }
        );
  }
  // context => 上下文对象
  // dependency => 依赖项,上述为入口地址
  // onModule => 往this.entries添加入口模块地址
  // callback => 回调
  _addModuleChain(context, dependency, onModule, callback) {
      const Dep = /** @type {DepConstructor} */ (dependency.constructor);
    // moduleFactory => NormalModuleFactory类
        const moduleFactory = this.dependencyFactories.get(Dep);
    // 调用NormalModuleFactory类中的create方法
    moduleFactory.create(
      // 传入的data
      {
        contextInfo: {
          issuer: "",
          compiler: this.compiler.name
        },
        context: context,
        dependencies: [dependency]
      },
      // 回调函数,主要工作就是往this.modules添加编译后的模块,以及设置缓存相关
      // module => 实例化NormalModules后的对象
      (err, module) => {
        // 如果有错误信息,直接结束
          if (err) {
                        this.semaphore.release();
                        return errorAndCallback(new EntryModuleNotFoundError(err));
        }
        ...
        // 里面有一句this.modules.push(module)代码
        // 往this.modules添加编译后的模块
        const addModuleResult = this.addModule(module);
        module = addModuleResult.module;
        // 添加入口依赖的模块
        onModule(module);
        dependency.module = module;
        ...
        if (addModuleResult.build) {
          // 开始编译模块
          // 内部调用module.build()方法
            this.buildModule(module, false, null, null, err => {
                            if (err) {
                  ...
              }
              ...
              // build 完成后
                            afterBuild();
                        });
        }
      }
    )
  }
}

06 => 实例化NormalModule,开始build

主要工作是执行了Tapable的factory钩子,实例化NormalModule类。最终走到了回调,回到了 05中

class NormalModuleFactory extends Tapable {
  constructor(context, resolverFactory, options) {
      super();
    ...
    this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
      // 调用resolver钩子
        let resolver = this.hooks.resolver.call(null);
      resolver(result, (err, data) => {
          if (err) return callback(err);
        if (!data) return callback();
        // 调用afterResolve钩子
        this.hooks.afterResolve.callAsync(data, (err, result) => {
            let createdModule = this.hooks.createModule.call(result);
                    if (!createdModule) {
                        if (!result.request) {
                            return callback(new Error("Empty dependency (no request)"));
                        }
            // 内部调用了NormalModule类
                        createdModule = new NormalModule(result);
                    }
          // 调用module钩子
                    createdModule = this.hooks.module.call(createdModule, result);
          return callback(null, createdModule);
        })
      })
    })
  }
    ...
  create(data, callback) {
    // 判断是否存在缓存,如果存在缓存直接返回
      const dependencies = data.dependencies;
        const cacheEntry = dependencyCache.get(dependencies[0]);
        if (cacheEntry) return callback(null, cacheEntry);
    // 获取属性
        const context = data.context || this.context;
        const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
        const request = dependencies[0].request;
    // 调用beforeResolve钩子
    this.hooks.beforeResolve.callAsync(
        {
                contextInfo,
                resolveOptions,
                context,
                request,
                dependencies
            },
      (err, result) => {
          if (err) return callback(err);
        // 调用factory钩子,factory钩子在constructor的时候就注册了
        const factory = this.hooks.factory.call(null);
        if (!factory) return callback();
        factory(result, (err, module) => {
                    if (err) return callback(err);
          // 设置缓存
                    if (module && this.cachePredicate(module)) {
                        for (const d of dependencies) {
                            dependencyCache.set(d, module);
                        }
                    }
          // 此时回到了 05中
                    callback(null, module);
                });
      }
    )
  }
}