乐趣区

关于javascript:webpack-流程解析2初始化完成

前言

上文说到 webpack 筹备好了参数,要创立 compiler对象了。
创立完之后,则会执行 compiler.run 来开始编译,本文将论述 new Compilercompiler.run()两头的过程。
整体过程都产生在 createCompiler 这个函数体内。

/**
 * @param {WebpackOptions} rawOptions options object
 * @returns {Compiler} a compiler
 */
const createCompiler = rawOptions => {

new Compiler

  • 在 new 之前,webpack 会实现一次根底参数的初始化,这里只给日志输入格局和 context 进行了赋值

    applyWebpackOptionsBaseDefaults(options);
  • webpack/lib/Compiler.js 是整个 webpack 的编译外围流程。
  • new Compiler 的时候先在 Tapable 注册了一堆钩子, 例如常见的watch-run,run, before-run, 等等。更多的钩子能够在这里查看。

初始化文件操作

new NodeEnvironmentPlugin({infrastructureLogging: options.infrastructureLogging}).apply(compiler);

这里是在拓展 compiler 对象,减少对文件的一些操作,例如 输出 , 输入 , 监听 , 缓存 等办法。

apply(compiler) {const { infrastructureLogging} = this.options;
        compiler.infrastructureLogger = createConsoleLogger({
            level: infrastructureLogging.level || "info",
            debug: infrastructureLogging.debug || false,
            console:
                infrastructureLogging.console ||
                nodeConsole({
                    colors: infrastructureLogging.colors,
                    appendOnly: infrastructureLogging.appendOnly,
                    stream: infrastructureLogging.stream
                })
        });
        compiler.inputFileSystem = new CachedInputFileSystem(fs, 60000);
        const inputFileSystem = compiler.inputFileSystem;
        compiler.outputFileSystem = fs;
        compiler.intermediateFileSystem = fs;
        compiler.watchFileSystem = new NodeWatchFileSystem(compiler.inputFileSystem);
        compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin", compiler => {if (compiler.inputFileSystem === inputFileSystem) {compiler.fsStartTime = Date.now();
                inputFileSystem.purge();}
        });
    }

这里的 fs,不是 nodejs 的 file system,是用了一个第三方包graceful-fs

注册插件

if (Array.isArray(options.plugins)) {for (const plugin of options.plugins) {if (typeof plugin === "function") {plugin.call(compiler, compiler);
        } else {plugin.apply(compiler);
        }
    }
}

接下来 webpack 会把 options 注册的插件,都注册一遍。传入 compiler 对象给插件外部应用,插件通过 compiler 提供的 hook,能够在编译全流程注册钩子的回调函数。同时有些 compiler 钩子又传入了 compilation 对象,又能够在资源构建的时候注册 compilation 钩子回调。

如何编写一个 webpack 插件

environment ready

插件注册完之后,webpack 又一次给 options 赋值了一次默认参数。为什么和后面的 applyWebpackOptionsBaseDefaults 一起呢。
这里调用了

applyWebpackOptionsDefaults(options);

又加了一波默认值。
加完之后调用了 environmentafterEnvironment两个钩子。

compiler.hooks.environment.call();
compiler.hooks.afterEnvironment.call();

注册内置插件

环境初始化之后,webpack 还须要执行一下它本人外部的默认插件。

new WebpackOptionsApply().process(options, compiler);

这里会依据你的配置,执行对应的插件。
挑几个和钩子有关系的讲讲,

解析 entry

new EntryOptionPlugin().apply(compiler);
compiler.hooks.entryOption.call(options.context, options.entry); 

这里就是生成构建所需的 entry 数据结构

/** @type {EntryOptions} */
        const options = {
            name,
            filename: desc.filename,
            runtime: desc.runtime,
            layer: desc.layer,
            dependOn: desc.dependOn,
            publicPath: desc.publicPath,
            chunkLoading: desc.chunkLoading,
            wasmLoading: desc.wasmLoading,
            library: desc.library
        };

而后再调用 EntryPluginapplay办法里注册 Compiler.hooks:compilation, make 这两个钩子函数。

注册 resloverFactory 钩子

webpack 自身的一些插件调用实现之后,会调用 afterPlugin 这个钩子。

compiler.hooks.afterPlugins.call(compiler);

接下来 webpack 在 compiler.resolverFactory 上注册了 resolveOptions 钩子

compiler.resolverFactory.hooks.resolveOptions
    .for("normal")
    .tap("WebpackOptionsApply", resolveOptions => {resolveOptions = cleverMerge(options.resolve, resolveOptions);
        resolveOptions.fileSystem = compiler.inputFileSystem;
        return resolveOptions;
});
compiler.resolverFactory.hooks.resolveOptions
    .for("context")
    .tap("WebpackOptionsApply", resolveOptions => {resolveOptions = cleverMerge(options.resolve, resolveOptions);
        resolveOptions.fileSystem = compiler.inputFileSystem;
        resolveOptions.resolveToContext = true;
        return resolveOptions;
    });
compiler.resolverFactory.hooks.resolveOptions
        .for("loader")
        .tap("WebpackOptionsApply", resolveOptions => {resolveOptions = cleverMerge(options.resolveLoader, resolveOptions);
                resolveOptions.fileSystem = compiler.inputFileSystem;
                return resolveOptions;
        });

这里的目标是为 Factory.createResolver 提供默认的参数对象(含有相干的 resolve 我的项目配置项)。
而后再调用 afterResolvers 钩子

compiler.hooks.afterResolvers.call(compiler);

初始化实现

到目前为止,compiler 对象上曾经有了足够的货色开始咱们的编译,就通知外界初始化实现,以 webpack 的调性,必然还有一个钩子的触发。

compiler.hooks.initialize.call();

结语

webpack 编译前的所有事件曾经交待分明,下一篇将开始启动编译。
文章中提到的各种钩子的注册,烦请读者记下来,后续在整个编译过程中,后面注册的一些钩子,常常会在你脱漏的中央触发,这也是调试 webpack 过程中的一个痛点。

退出移动版