webpack执行过程

webpack-cli

执行过程

1.webpack.config.js,shell options参数解析
2.new webpack(options)
3.run() 编译的入口方法
4.compile() 出发make事件
5.addEntry() 找到js文件,进行下一步模块绑定
6._addModuleChain() 解析js入口文件,创建模块
7.buildModule() 编译模块,loader处理与acorn处理AST语法树
8.seal() 每一个chunk对应一个入口文件
9.createChunkAssets() 生成资源文件
10.MainTemplate.render() __webpack__require()引入
11.ModuleTemplate.render() 生成模版
12.module.source() 将生成好的js保存在compilation.assets中
13.Compiler.emitAssets()通过emitAssets将最终的js输出到output的path中

参数解析

(function(){
    yargs.options({...})
    
    yargs.parse(process.argv.slice(2), (err, argv, output) => {...})
})()

加载webpack.config.js

(function(){
    ...
    yargs.parse(process.argv.slice(2), (err, argv, output) => {
        ...
        //解析argv,拿到配置文件参数
       let options =  require("./convert-argv")(argv);
       function processOptions(options){
           ...
       } 
        
       processOptions(options);
    })
})()

执行webpack()

(function(){
    ...
    yargs.parse(process.argv.slice(2), (err, argv, output) => {
        ...
        //解析argv,拿到配置文件参数
       let options =  require("./convert-argv")(argv);
       function processOptions(options){
           ...
           const webpack = require("webpack");
           compiler = webpack(options);   
       } 
        
       processOptions(options);
    })
})()

webpack.js

const webpack = (options, callback) => {
    
    //验证webpack.config.js合法性
    const webpackOptionsValidationErrors = validateSchema(
        webpackOptionsSchema,
        options
    );
    
    /*
        [
          { entry: './index1.js', output: { filename: 'bundle1.js' } },
          { entry: './index2.js', output: { filename: 'bundle2.js' } }
        ]
    */
    if (Array.isArray(options)) {
        compiler = new MultiCompiler(options.map(options => webpack(options)));
    } else if(typeof options === "object"){
        ...
        //创建一个comiler对象
        compiler = new Compiler(options.context);
        
        //往comiler中注册插件
        new NodeEnvironmentPlugin().apply(compiler);
        
        //执行config中配置的插件
        if (options.plugins && Array.isArray(options.plugins)) {
            for (const plugin of options.plugins) {
                if (typeof plugin === "function") {
                    plugin.call(compiler, compiler);
                } else {
                    plugin.apply(compiler);
                }
            }
        }
        
        //执行插件environment生命周期钩子方法
        compiler.hooks.environment.call();
        compiler.hooks.afterEnvironment.call();
        //执行webpack内置插件
        compiler.options = new
        WebpackOptionsApply().process(options, compiler);
    }else {
        throw new Error("Invalid argument: options");
    }
    
    if (callback) {
        ...
        //调用compiler.run开始编译
        compiler.run(callback);
    }
    //将compiler对象返回
    return compiler
}
//NodeEnvironmentPlugin.js
class NodeEnvironmentPlugin {
    apply(compiler) {
        ...
        compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin", compiler => {
            if (compiler.inputFileSystem === inputFileSystem) inputFileSystem.purge();
        });
    }
}
module.exports = NodeEnvironmentPlugin;
class WebpackOptionsApply extends OptionsApply {
    constructor() {
        super();
    }
    process(options, compiler) {
        //挂载配置,执行插件
        let ExternalsPlugin;
        compiler.outputPath = options.output.path;
        compiler.recordsInputPath = options.recordsInputPath || options.recordsPath;
        compiler.recordsOutputPath =
            options.recordsOutputPath || options.recordsPath;
        compiler.name = options.name;
        
        new EntryOptionPlugin().apply(compiler);
        new HarmonyModulesPlugin(options.module).apply(compiler);
        new LoaderPlugin().apply(compiler);
        ...
    }
}

module.exports = WebpackOptionsApply;

compiler.run() 开始编译

class Compiler extends Tapable{
    constructor(context){
        ...
    }
    watch(){...}
    run(callback){
        ...
        const onCompiled = (err, compilation){
            ...
        } 
        //执行生命周期钩子
        this.hooks.beforeRun.callAsync(this, err => {
              ...
            this.hooks.run.callAsync(this, err =>{
                this.readRecords(err =>{
                    ...
                    //开始编译
                    this.compile(onCompiled);
                })
            }
        }
    }
    compile(callback) {
        //拿到参数
        const params = this.newCompilationParams();
        //执行编译前钩子
        this.hooks.beforeCompile.callAsync(params, err => {
            ...
            
            //创建compilation对象
            const compilation = this.newCompilation(params);
            
            //开始构建模块对象
            this.hooks.make.callAsync(compilation, err =>{
                
            })
        }
    }
    createCompilation() {
        //创建comilation对象
        return new Compilation(this);
    }
    newCompilation(params) {
        //调用创建compilation对象方法
        const compilation = this.createCompilation();
    }
}

module.exports = Compiler;

创建 Compilation()

class Compilation extends Tapable {
    constructor(compiler) {
        super();
        ...
        //初始化配置
        this.compiler = compiler;
        this.resolverFactory = compiler.resolverFactory;
        this.inputFileSystem = compiler.inputFileSystem;
        this.requestShortener = compiler.requestShortener;
        
        //初始化模版
        this.mainTemplate = new MainTemplate(this.outputOptions);
        this.chunkTemplate = new ChunkTemplate(this.outputOptions);
        this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate(
            this.outputOptions
        );
    }
}
class MainTemplate extends Tapable {
    this.hooks.requireExtensions.tap("MainTemplate", (source, chunk, hash) => {
            const buf = [];
            const chunkMaps = chunk.getChunkMaps();
            // Check if there are non initial chunks which need to be imported using require-ensure
            if (Object.keys(chunkMaps.hash).length) {
                buf.push("// This file contains only the entry chunk.");
                buf.push("// The chunk loading function for additional chunks");
                buf.push(`${this.requireFn}.e = function requireEnsure(chunkId) {`);
                buf.push(Template.indent("var promises = [];"));
                buf.push(
                    Template.indent(
                        this.hooks.requireEnsure.call("", chunk, hash, "chunkId")
                    )
                );
                buf.push(Template.indent("return Promise.all(promises);"));
                buf.push("};");
            } else if (
                chunk.hasModuleInGraph(m =>
                    m.blocks.some(b => b.chunkGroup && b.chunkGroup.chunks.length > 0)
                )
            ) {
                // There async blocks in the graph, so we need to add an empty requireEnsure
                // function anyway. This can happen with multiple entrypoints.
                buf.push("// The chunk loading function for additional chunks");
                buf.push("// Since all referenced chunks are already included");
                buf.push("// in this file, this function is empty here.");
                buf.push(`${this.requireFn}.e = function requireEnsure() {`);
                buf.push(Template.indent("return Promise.resolve();"));
                buf.push("};");
            }
            buf.push("");
            buf.push("// expose the modules object (__webpack_modules__)");
            buf.push(`${this.requireFn}.m = modules;`);

            buf.push("");
            buf.push("// expose the module cache");
            buf.push(`${this.requireFn}.c = installedModules;`);

            buf.push("");
            buf.push("// define getter function for harmony exports");
            buf.push(`${this.requireFn}.d = function(exports, name, getter) {`);
            buf.push(
                Template.indent([
                    `if(!${this.requireFn}.o(exports, name)) {`,
                    Template.indent([
                        "Object.defineProperty(exports, name, { enumerable: true, get: getter });"
                    ]),
                    "}"
                ])
            );
            buf.push("};");

            buf.push("");
            buf.push("// define __esModule on exports");
            buf.push(`${this.requireFn}.r = function(exports) {`);
            buf.push(
                Template.indent([
                    "if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {",
                    Template.indent([
                        "Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });"
                    ]),
                    "}",
                    "Object.defineProperty(exports, '__esModule', { value: true });"
                ])
            );
            buf.push("};");

            buf.push("");
            buf.push("// create a fake namespace object");
            buf.push("// mode & 1: value is a module id, require it");
            buf.push("// mode & 2: merge all properties of value into the ns");
            buf.push("// mode & 4: return value when already ns object");
            buf.push("// mode & 8|1: behave like require");
            buf.push(`${this.requireFn}.t = function(value, mode) {`);
            buf.push(
                Template.indent([
                    `if(mode & 1) value = ${this.requireFn}(value);`,
                    `if(mode & 8) return value;`,
                    "if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;",
                    "var ns = Object.create(null);",
                    `${this.requireFn}.r(ns);`,
                    "Object.defineProperty(ns, 'default', { enumerable: true, value: value });",
                    "if(mode & 2 && typeof value != 'string') for(var key in value) " +
                        `${this.requireFn}.d(ns, key, function(key) { ` +
                        "return value[key]; " +
                        "}.bind(null, key));",
                    "return ns;"
                ])
            );
            buf.push("};");

            buf.push("");
            buf.push(
                "// getDefaultExport function for compatibility with non-harmony modules"
            );
            buf.push(this.requireFn + ".n = function(module) {");
            buf.push(
                Template.indent([
                    "var getter = module && module.__esModule ?",
                    Template.indent([
                        "function getDefault() { return module['default']; } :",
                        "function getModuleExports() { return module; };"
                    ]),
                    `${this.requireFn}.d(getter, 'a', getter);`,
                    "return getter;"
                ])
            );
            buf.push("};");

            buf.push("");
            buf.push("// Object.prototype.hasOwnProperty.call");
            buf.push(
                `${
                    this.requireFn
                }.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };`
            );

            const publicPath = this.getPublicPath({
                hash: hash
            });
            buf.push("");
            buf.push("// __webpack_public_path__");
            buf.push(`${this.requireFn}.p = ${JSON.stringify(publicPath)};`);
            return Template.asString(buf);
        });
}

make开始构建

 //开始构建模块对象
this.hooks.make.callAsync(compilation, err =>{
                
})
//SingleEntryPlugin 监听make
class SingleEntryPlugin {
    apply(compiler) {
        compiler.hooks.make.tapAsync(
            "SingleEntryPlugin",
            (compilation, callback) => {
                const { entry, name, context } = this;
                
                //创建依赖
                const dep = SingleEntryPlugin.createDependency(entry, name);
                //添加入口文件
                compilation.addEntry(context, dep, name, callback);
            }
        );
    }
}
//Compilation.js
class Compilation extends Tapable {
    addEntry(context, entry, name, callback) {
        ...
            this._addModuleChain(
            context,
            entry,
            module => {
                this.entries.push(module);
            },
            (err, module) => {
            ...
            }
        );
    }
    _addModuleChain(context, dependency, onModule, callback) {
        ...
        //获取模块工厂
        const moduleFactory = this.dependencyFactories.get(Dep);
        
        this.semaphore.acquire(() => {
            ...
            //创建模块
            moduleFactory.create(
                {
                    contextInfo: {
                        issuer: "",
                        compiler: this.compiler.name
                    },
                    context: context,
                    dependencies: [dependency]
                },...)
        }
    }
}
class NormalModuleFactory extends Tapable {
    ...
    create(data, callback) {
        ...
        this.buildModule(module, false, null, null, err => {
        }
    }
    buildModule(module, optional, origin, dependencies, thisCallback) {
        ...
        //开始编译
        module.build(
            this.options,
            this,
            this.resolverFactory.get("normal", module.resolveOptions),
            this.inputFileSystem,...)
    }
}
//NodmalModule
doBuild(options, compilation, resolver, fs, callback) {
        const loaderContext = this.createLoaderContext(
            resolver,
            options,
            compilation,
            fs
        );
        ...
        //开始运行loader
        runLoaders(
            {
                resource: this.resource,
                loaders: this.loaders,
                context: loaderContext,
                readResource: fs.readFile.bind(fs)
            },
            (err, result) => {
            
            );
         )  }

猜你喜欢

转载自www.cnblogs.com/pluslius/p/10271537.html
今日推荐