美文网首页
从零开始手写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