webpack源码深入--- compiler.compile

发布于:2024-07-03 ⋅ 阅读:(12) ⋅ 点赞:(0)

compiler.compile

compiler.compile

Compiler.prototype.compile方法总览

class Compiler {
    // ...
    compile(callback) {
        // 调用compiler.newCompilationParams()获取创建compilation实例对象的参数
        const params = this.newCompilationParams();
        // 传入参数,触发this.hooks.beforeCompile的钩子
        this.hooks.beforeCompile.callAsync(params, err => {
            // 触发compiler.hooks.compile钩子
            this.hooks.compile.call(params);
            // 创建compilation对象
            const compilation = this.newCompilation(params);
            // 触发make的钩子
            this.hooks.make.callAsync(compilation, err => {
                // 触发finishMake钩子
                this.hooks.finishMake.callAsync(compilation, err => {
                    process.nextTick(() => {
                        // 触发finish钩子
                        compilation.finish(err => {
                            compilation.seal(err => {
                                this.hooks.afterCompile.callAsync(compilation, err => {
                                  return callback(null, compilation);
                                });
                            });
                        });
                    });
                });
            });
        });
    }
}
compiler.newCompilationParams()方法
class Compiler {
    // ...
    newCompilationParams() {  
        const params = {  
            // 常规模块工厂
            normalModuleFactory: this.createNormalModuleFactory(),  
            // 上下文模块中的工厂类
            contextModuleFactory: this.createContextModuleFactory()  
        };  
        return params;  
    }
}
  1. 普通模块: ESM或者commonjs或者cmd/amd的模块规范产物
  2. 上下文模块: 当编译的时候无法具体确定某一个模块,可以直接在webpack上引用一整个目录,把目录中的所有模块都打包,然后等到运行的时候去具体某一个的模块, 这种模块叫上下文模块
createNormalModuleFactory方法
class Compiler { 
    // ...
    createNormalModuleFactory() {
        const normalModuleFactory = new NormalModuleFactory({});   
        this.hooks.normalModuleFactory.call(normalModuleFactory);
        return normalModuleFactory;
    }
}
  • 定义 factory/resolve 等 NMF 与模块创建相关生命周期钩子
  • 格式化webpack.config.js中定义的loader
  • 定义loader和模块的加载解析工作
  • 定义模块构建相关工作
    createcontextmodulefactory方法和上面的方法类似
compiler.hooks.beforeCompile.call方法
  1. DllReferencePlugin:DLL引用插件,就是动态链接库的引用库,在beforeCompile阶段尝试manifest.json,就是DLL清单列表
  2. ProgressPlugin: webpack的构建进度
  3. lazycompilationPlugin: 用于在webpack beforeCompile中针对一些懒加载的入口进行处理。
compiler.hooks.compile

触发compiler.hooks.compile钩子,有几个插件订阅在这个阶段

  1. DLLReferencePlugin: 在compiler.hooks.compile阶段根据配置生成externals对象。
  2. ExternalsPlugin: 处理 external 选项,在该阶段为配置的各个 external 配置 ExternalModuleFactoryPlugin,该插件订阅 NMF 的 factory 阶段为配置的外部模块创建模块,以便这些外部模块可以正常的加入的 webpack runtime 的工作流中
compiler.newCompilation(params)

创建compilation对象,该方法接受前面的params对象

class Compiler { 
    // ...
    newCompilation(params) {
        const compilation = this.createCompilation(params);
        compilation.name = this.name;
        compilation.records = this.records;
        this.hooks.thisCompilation.call(compilation, params);
        this.hooks.compilation.call(compilation, params);
        return compilation;
    }	
    // 创建compilation
    createCompilation(params) {
        this._cleanupLastCompilation();
        return (this._lastCompilation = new Compilation(this, params));
    }
}

compilation是webpack中最重要的对象,相当于是处理具体编译相关事宜的对象。包括模块构建,生成moduleGraph,chunkGraph,以及模块,chunk的相关优化的工作

compiler.hooks.make

有了compilation之后,触发compiler.hooks.make钩子,进入了模块构建阶段。webpackoptionsapply.prototype.process方法注册了EntryOptionsApply,EntryPlugin为每个entry注册了EntryPlugin,而EntryPlugin又订阅了compiler.hooks.make钩子

EntryOptionPlugin

EntryOption订阅了compiler.hooks.entryOption钩子,当钩子触发调用EntryOptionPlugin.applyEntryPlugin根据不同的类型的入口应用不同的类型的入口插件

class EntryOptionPlugin {
    apply(compiler) {
        // 订阅 compiler.hooks.entryOpion 钩子
        compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
            EntryOptionPlugin.applyEntryOption(compiler, context, entry);
            return true;
        });
    }
    static applyEntryOption(compiler, context, entry) {
        if (typeof entry === "function") {
            // 处理动态入口场景
            const DynamicEntryPlugin = require("./DynamicEntryPlugin");
            new DynamicEntryPlugin(context, entry).apply(compiler);
        } else {
            // 初始化 EntryPlugin 插件初始化普通入口
            const EntryPlugin = require("./EntryPlugin");
            for (const name of Object.keys(entry)) {
                const desc = entry[name];
                const options = EntryOptionPlugin.entryDescriptionToOptions(
                    compiler,
                    name,
                    desc
                );
                for (const entry of desc.import) {
                    new EntryPlugin(context, entry, options).apply(compiler);
                }
            }
        }
    }
}
EntryPlugin
class EntryPlugin {
    constructor(context, entry, options) {
        this.context = context;
        this.entry = entry;
        this.options = options || "";
    }
    apply(compiler) {
        compiler.hooks.compilation.tap(
            "EntryPlugin",
            (compilation, { normalModuleFactory }) => {
                compilation.dependencyFactories.set(
                    EntryDependency,
                    normalModuleFactory
                );
            }
        );
        const { entry, options, context } = this;
        const dep = EntryPlugin.createDependency(entry, options);
        compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => {
            compilation.addEntry(context, dep, options, err => {
                callback(err);
            });
        });
    }
}
  1. 订阅compiler.hooks.compilation钩子,向EntryDependency设置工厂类为normalModuleFactory
  2. 创建入口依赖并订阅compiler.hooks.make钩子