webpack 流程解析(4): 開始構建

csywweb發表於2021-10-18

前言

準備工作做了三遍文章,現在、立刻、馬上,我們進入構建流程的分析!

構建入口

這個過程還是在compiler.compiler函式裡,

    // 在這之前new了一個 compilation 物件
    this.hooks.make.callAsync(compilation, err => {
            logger.timeEnd("make hook");
            if (err) return callback(err);
            logger.time("finish make hook");
            this.hooks.finishMake.callAsync(compilation, err => {
                logger.timeEnd("finish make hook");
                if (err) return callback(err);
                process.nextTick(() => {
                    logger.time("finish compilation");
                    compilation.finish(err => {
                        logger.timeEnd("finish compilation");                                  if (err) return callback(err);
                        logger.time("seal compilation");
                        compilation.seal(err => {
                            //dosomething
                        });
                    });
                });
            });
    });

這裡觸發了make鉤子註冊的回撥,還記得我在初始化部分提到的EntryPlugin嗎?在這裡註冊了一個鉤子回撥,觸發了 compilation.addEntry

compilation.addEntry(context, dependency, name, callback); //其中 dependency 為 EntryDependency 例項。

addEntry

addEntry 做了這麼幾件事:

  • 生成 EntryData
  • 呼叫compilation鉤子addEntry
  • 執行 compilation.addModule

addModule

addModule 根據dep,拿到對應的 moduleFactory, 然後執行handleModuleCreation, 把 moduleFactorydependency等資料塞入一個佇列factorizeQueue

獲取moduleFactory

const Dep = /** @type {DepConstructor} */ (dependency.constructor);
const moduleFactory = this.dependencyFactories.get(Dep);

this.dependencyFactories是一個 Map, 那麼他是什麼時候set的呢?答案還是在初始化部分提到的EntryPlugin中。

** 塞入佇列

獲取到依賴和模組的編譯方法之後,塞入factorizeQueue佇列

this.factorizeModule({
    currentProfile,
    factory,
    dependencies,
    factoryResult: true,
    originModule,
    contextInfo,
    context
},
() => { // dosomethine})
// Workaround for typescript as it doesn't support function overloading in jsdoc within a class
Compilation.prototype.factorizeModule = 
/** @type {{
    (options: FactorizeModuleOptions & { factoryResult?: false }, callback: ModuleCallback): void;
    (options: FactorizeModuleOptions & { factoryResult: true }, callback: ModuleFactoryResultCallback): void;
}} */ 
(
    function (options, callback) {
        this.factorizeQueue.add(options, callback);
    }
);

看到這裡,有點沒有頭緒,add之後在整個 compilation 裡沒有找到類似於 factorizeQueue.startfactorizeQueue.run 之類的程式碼。一起去看看factorizeQueue 內部幹了啥

factorizeQueue

this.factorizeQueue = new AsyncQueue({
    name: "factorize",
    parent: this.addModuleQueue,
    processor: this._factorizeModule.bind(this)
});

factorizeQueueAsyncQueue 的例項。AsyncQueue主要是做了一個佇列控制。佇列長度根據外部傳入的parallelism來控制,factorizeQueue沒有傳,這裡預設為1。

如果條件ok,在AsyncQueue的內部會呼叫_processor

this._processor(entry.item, (e, r) => {
    inCallback = true;
    this._handleResult(entry, e, r);
});

這裡就呼叫到_factorizeModule,接下來執行factory.create,開始reslove!

結語

到這裡我們已經瞭解到webpack是如何使用配置中的entry屬性,獲取到modulefactory,下一篇將介紹reslove過程。

相關文章