美文网首页
从零开始手写webpack(2)

从零开始手写webpack(2)

作者: laibao | 来源:发表于2019-05-23 17:55 被阅读0次

上一版做了什么

  1. 实现了一个js打包器

这一版做了什么

  1. 增加loader支持
  2. 增加plugin支持

为什么要增加loader和plugin

在上一篇文章中提到,webpack 就是一个 打包 js代码的打包器。至于webpack能打包 图片、css、less、scss 等其他文件, 那都是loader或者plugin的功能。所以,为了打包器更强大,需要增加loaderplugin的支持

不足的点

我只是写了一个简单的实现,真正的webpack比我这个强大太多,但是基本原理是一样的。

先来一发整体工作流程图吧

webpack工作原理.png

再来一个架构图

webpack架构图.png

工作流程

通过上述两个图,可以大概描述出webpack的工作原理了。通过loaderplugin的加持,webpack可以完成各种各样的工作。

loader支持

编译器在获取源码的时候,即可对源码进行操作,此时,loader便可以排上用场了,在loader中,对源码可以做一些操作,然后讲源码返回。 这也是webpackloader的处理方式。具体修改为,在上一版的编译器的构建模块函数中增加代码。

getSource(modulePath) {
        const rules = this.config.module.rules;
        let content = fs.readFileSync(modulePath, 'utf8');
        for(let i = 0; i < rules.length; i++){
            const rule = rules[i];
            const { test, use } = rule;
            let len = use.length - 1;
            if (test.test(modulePath)) {
                const normalLoader = () => {
                    // loader 获取对应的loader函数
                    const loader = require(use[len--]);
                    content = loader(content);
                    if (len >= 0) {
                        normalLoader();
                    }
                }
                normalLoader();
            }
        }
        return content;
    }

plugin支持

针对plugin的支持,采用了和webpack一样的模式,使用tapable这个库

简单介绍一下tapable这个库。这是库提供了一些观察者处理方法,有同步hooks异步hooks. 具体使用方法,请看 tapable

所以在编译器run方法执行之前,我们需要注册所有的观察者. 则有了代码:

constructor(config) {
        this.config = config;
        // 需要保存入口文件的路径
        this.entryId;
        // 需要保存所有模块的依赖
        this.modules = {};
        // 入口路径
        this.entry = config.entry;
        // 工作路径
        this.root = process.cwd();
        this.hooks = {
            entryOption: new SyncHook(),
            compile: new SyncHook(),
            afterCompile: new SyncHook(),
            afterPlugins: new SyncHook(),
            run: new SyncHook(),
            emit: new SyncHook(),
            done: new SyncHook()
        }

        const { plugins = [] } = this.config;
        // 注册所有的plugin
        if (Array.isArray(plugins)) {
            plugins.forEach(plugin => {
                plugin.apply(this);
            });
        }
        // 调用plugin注册完的钩子
        this.hooks.afterPlugins.call();
    }

剩下的,我们就是需要在编译器的各个步骤来触发钩子函数了.

例如, 在编译开始之前、编译进行时、编译完成后、文件发射前、文件发射后....

这个版本里面我只做了几个简单的触发

run() {
        this.hooks.run.call();
        this.hooks.compile.call();
        // 执行并且创建模块的依赖关系
        this.buildModule(path.resolve(this.root, this.entry), true);
        this.hooks.afterCompile.call();
        // 发射一个打包后的文件
        this.emitFile();
        this.hooks.emit.call();
        this.hooks.done.call();
    }

完整源码

待上传到github

相关文章

网友评论

      本文标题:从零开始手写webpack(2)

      本文链接:https://www.haomeiwen.com/subject/hhwbzqtx.html