webpack源码分析(一)— Tapable插件架构

在前面的文章webpack不适合多页面应用?你写的插件还不够多中提到过,webpack核心使用了Tapable实现事件的发布订阅处理的插件架构(Tapable中文文档),今天就具体来分析下webpack基于Tapable的插件架构

找到代码入口

  • 想必你已经使用过npm install webpack命令下载过webpack,那么在你的node_modules目录下找到webpack。
  • npm模块的入口文件可以通过package.json中的"main": "lib/webpack.js"找到,当你通过reqire引用模块的时候,其实定位到的就是这个文件。一般情况下,我们会在命令行直接使用webpack命令去执行打包,这个时候执行的就是bin/webpack.js了,这个命令只是在调用lib/webpack.js之前处理一些命令行参数,殊途同归。
  • 打开lib/webpack.js。webpack.js除了使用exportPlugins导出很多插件类(方便外部调用),最重要的事情就是创建compiler对象(如果options是数组的话,每个元素创建一个)
    //创建compiler对象
    compiler = new Compiler();
    //后面这几句代码,得看了compiler再回来看看了
    compiler.options = options;
    compiler.options = new WebpackOptionsApply().process(options, compiler);
    new NodeEnvironmentPlugin().apply(compiler);
    compiler.applyPlugins("environment");
    compiler.applyPlugins("after-environment");

这个时候我们的视线得转移到compiler上了

植入Tapable

打开lib/Compiler.js

    function Compiler() {

        //传入作用域,调用Tapable的构造函数
        Tapable.call(this);

        this.outputPath = "";
        this.outputFileSystem = null;
        this.inputFileSystem = null;

        this.recordsInputPath = null;
        this.recordsOutputPath = null;
        this.records = {};

        this.fileTimestamps = {};
        this.contextTimestamps = {};

        this.resolvers = {
            normal: new Resolver(null),
            loader: new Resolver(null),
            context: new Resolver(null)
        };
        this.parser = new Parser();

        this.options = {};
    }
    module.exports = Compiler;

    //复制一份Tapable的原型
    Compiler.prototype = Object.create(Tapable.prototype);
    Compiler.prototype.constructor = Compiler;

如果你阅读过Tapable中文文档,你应该对这个mix的方式不会陌生,tapable的原理其实也不复杂

声明一个全局的变量this._plugins = {},插件中使用plugin(name, fn)方法给事件name注册处理方法fn,多次注册形成了事件name的监听链,当事件name触发的时候,执行这些处理方法。处理方法的执行顺序和执行方式依据事件name的触发方式的不同而不同

这个时候compiler已经具备Tapable的所有属性和方法了,我们再回到lib/webpack.js来看看创建了Compiler对象后的几行代码

说实话不太欣赏这种在对象外面初始化的设计模式,读代码的时候你得跳来跳去,我更倾向于通过构造函数传入options,在对象内进行初始化工作。(仅代表个人的想法)

    //给对象参数赋值
    compiler.options = options;
    //传入options和compiler执行WebpackOptionsApply的process想法
    //这个方法对参数进行了处理,并且注入了大量的插件
    compiler.options = new WebpackOptionsApply().process(options, compiler);
    //注册nodeEveironmentPlugin插件
    new NodeEnvironmentPlugin().apply(compiler);
    //触发environment和after-environment事件
    compiler.applyPlugins("environment");
    compiler.applyPlugins("after-environment");

webpack很多核心功能本身就是以插件的形式开发的,打开lib/WebpackOptionsApply就会发现,在这个方法中,除了处理参数,就是把一个个插件注册到compiler中

compiler.applyPlugins("environment")以一种最简单的并行处理的方式去去触发事件environment事件,所有注册的处理方法并行执行,相互独立互不干扰,并且不需要给处理方法传入参数。

而有些事件的触发方式要复杂一些,例如complier触发emit的方式

    this.applyPluginsAsync("emit", compilation, function(err) {
        if(err) return callback(err);
        outputPath = compilation.getPath(this.outputPath);
        this.outputFileSystem.mkdirp(outputPath, emitFiles.bind(this));
    }.bind(this));

这段代码:
触发事件emit,传入参数compilation对象,串行的调用注册在事件emit上的处理函数(先入先出),倘若某一个处理函数报错,则执行传入的function(err),后续的处理函数将不被执行,否则最后一个处理函数调用function()。插件注册此类事件,处理函数需要调用callback,这样才能保证监听链的正确执行。所以为了在写自定义插件的时候能正确的监听事件,非常有必要仔细读Tapable中文文档(虽然本文已经多次提到这个文档,但是还是有必要再进行一次提醒)

webpack中另外一个重要的对象compilation使用了同样的方式植入了tapable

总结

咱们分析webpack源码主要有两个原因:一是为了学习优秀的代码的设计方法,二是为了编写webpack自定义插件的时候能够游刃有余。不管从哪一点来说,Tapable面向切面的插件思想,都是值得我们琢磨的(在我的一个项目中还真的从webpack把Tapable借鉴过来了)

后续会继续更新对webpack源码的进一步分析,欢迎关注,共同学习。有问题请评论或发简信,如果你觉得文章对你有所帮助,请不要吝惜你的喜欢,当然,给我打赏我也不会客气的~~。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • GitChat技术杂谈 前言 本文较长,为了节省你的阅读时间,在文前列写作思路如下: 什么是 webpack,它要...
    萧玄辞阅读 12,613评论 7 110
  • 说在前面:这些文章均是本人花费大量精力研究整理,如有转载请联系作者并注明引用,谢谢本文的受众人群不是webpack...
    RockSAMA阅读 6,798评论 2 7
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 无意中看到zhangwnag大佬分享的webpack教程感觉受益匪浅,特此分享以备自己日后查看,也希望更多的人看到...
    小小字符阅读 8,081评论 7 35
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,358评论 6 343