webpack源码解析---addEntry

addEntry

EntryPlugin的注册

webpack会从入口开始解析依赖。

  1. WebpackOptionsApply

    javascript 复制代码
    new WebpackOptionsApply().process(compiler, options);
    class WebpackOptionsApply {
        constructor () {}
        process () {
           // 注册 EntryOptionPlugin 
            new EntryOptionPlugin().apply(compiler);
        }
    }
  2. EntryOptionPlugin

    EntryOptionPlugin的作用是注册compiler.hooks.entryOption钩子,当钩子被触发的时候,调用EntryOptionPlugin.applyEntryOption方法注册DynamicEntryPlugin或者EntryPlugin

    javascript 复制代码
    class EntryOptionPlugin {
        apply(compiler) {
            compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
                // 调用静态方法 applyEntryOption
                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 {
                // 普通入口
                const EntryPlugin = require("./EntryPlugin");
                for (const name of Object.keys(entry)) {               
                        for (const entry of desc.import) {
                            new EntryPlugin(context, entry, options).apply(compiler);
                        }
                }
            }
        }
    }
  3. EntryPlugin

    • 订阅了compiler.hooks.compilation钩子,触发时设置EntryDependency的ModuleFactory normalModuleFactory工厂,这个用于创建入口模块
    • 订阅compiler.hooks.make钩子,触发的时候调用compilation.addEntry方法,将入口模块加载到factorizeQueue队列,这样依赖就启动了以入口模块为七点的模块构建流程。
    javascript 复制代码
    class EntryPlugin {
        constructor(context, entry, 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);
                });
            });
        }
    }
  4. EntryPlugin的触发

    调用compiler.run() -> compiler.compile()方法触发compiler.hooks.make钩子,进而触发EntryPlugin钩子

javascript 复制代码
class Compiler {
    compile(callback) {
        this.hooks.beforeCompile.callAsync(params, err => {
            this.hooks.compile.call(params);
            this.hooks.make.callAsync(compilation, err => {})
        })
    }
}

流程为: npm run build -> webpack -> webpack-cli -> compiler.run() -> compiler.compile() -> compiler.hooks.make.callAsync() -> EntryPlugin -> compilation.addEntry()

compilation.addEntry方法的讲解

方法参数

  1. context: 上下文目录,构建中就是当前目录的项目目录
  2. entry: 入口对象,结合EntryPlugin可以看到传入的路由,Entryplugin.createDependency方法返回值对象
  3. optionsOrName: 选项或者名字
  4. callback: entry的回调函数,用于和compiler通信进行后续的流程
javascript 复制代码
class Compilation {
    addEntry(context, entry, optionsOrName, callback) {
        const options =
            typeof optionsOrName === "object"
                    ? optionsOrName
                    : { name: optionsOrName };
        this._addEntryItem(context, entry, "dependencies", options, callback);
    }
    _addEntryItem(context, entry, target, options, callback) {}
}

方法逻辑

  1. 标准化处理options,根据optionsOrName格式化成不同的对象
  2. 调用compilation._addEntryItem()方法
compilation._addEntryItem
  • context: webpack构建上下文目录,以及项目目录
  • entry: 入口对象,上文中的EntryPlugin.createDependency()返回的EntryDependency类型的实例对象
  • target: 目标类型,用于在entryData中分类类型进行缓存的标志
  • options: webpack的配置对象或者是nameOptions对象,由compilation.addEntry标准化
  • callback: 回调函数,用于和compiler进行通信使用
javascript 复制代码
class Compilation {
    _addEntryItem(context, entry, target, options, callback) {
        // 根据options是否由name属性或者是compilation.entries或者是compilation.globalEntry中尝试获取缓存的入口entryData
        const { name } = options;
        let entryData =
            name !== undefined ? this.entries.get(name) : this.globalEntry;
        // 没有entryData的时候,会构建entryData对象,将其缓存到compilation.entries,另外entryData.dependency设置为初始的entry入口对象
        if (entryData === undefined) {
            entryData = {
                dependencies: [],
                includeDependencies: [],
                options: {
                    name: undefined,
                    ...options
                }
            };
            entryData[target].push(entry);
            this.entries.set(name, entryData);
        } else {
            // 这里不成立,先忽略
        }    
        // 触发this.hooks.addEntry钩子
        this.hooks.addEntry.call(entry, options);
        // this.addModuleTree方法
        this.addModuleTree(/* ... */);
    }
}

callback回调 => _addEntryItem的回调

javascript 复制代码
addEntry(context, entry, optionsOrName, callback) {
    this._addEntryItem(context, entry, "dependencies", options, callback);
}

compilation.addModuleTree

  • context: 上下文,当前项目的目录
  • dependency: 依赖对象
  • contextInfo: 上下文对象
javascript 复制代码
class Compilation {
    // ...
    addModuleTree({ context, dependency, contextInfo }, callback) {
        const Dep =  dependency.constructor;
        const moduleFactory = this.dependencyFactories.get(Dep);
        this.handleModuleCreation(
            {
                factory: moduleFactory,
                dependencies: [dependency],
                originModule: null,
                contextInfo,
                context
            },
            (err, result) => {
                if (err && this.bail) {
                
                } else if (!err && result) {
                    callback(null, result);
                } else {
                    callback();
                }
            }
        );
    }
}

执行模块以及其依赖的子模块的构建,构建工作

相关推荐
Marshmallowc2 分钟前
CSS 布局原理:为何“负边距”是栅格系统的基石?
前端·css·面试
Rysxt_4 分钟前
Vue 3 项目核心:App.vue 文件的作用与配置详解
前端·javascript·vue.js
洛阳纸贵10 分钟前
JAVA高级工程师--Maven父子关系专题
java·前端·maven
imkaifan13 分钟前
10、vue3中针对图片的处理
前端·javascript·vue.js
柯南二号14 分钟前
【大前端】【iOS】iOS 使用 Objective-C 绘制几大常见布局(UIKit / Core Graphics 实战)
前端·ios
爱干饭的boy15 分钟前
MacBook安装node.js/maven/mysql
mysql·node.js·maven
千里马-horse22 分钟前
Checker Tool
c++·node.js·napi
invicinble24 分钟前
对于使用html去进行前端开发的全面认识,以及过度到vue开发
前端·javascript·vue.js
我这一生如履薄冰~24 分钟前
element-plus去除el-dropdown组件当鼠标移入文本时会出现边框
前端·elementui·vue
小果子^_^29 分钟前
div或按钮鼠标经过或鼠标点击后效果样式
前端·css·计算机外设