• 笑着看完webpack原理【webpack厂长第一视角】


    开场白

    大家好,我是Webpack,AKA打包老炮,我的slogan是:“打天下的包,让Rollup无包可打”。

    今天我要带来的才艺是:剖析打包的艺术
    
    • 1

     故事还要从一次npm包工头拉我进项目开始…

      那是一个阳光明媚的凌晨,我的眼前是一个正在脱头发的精神程序小哥。他写了一个很简单的webpack+vue项目,企图通过这个项目来了解我的全部。

      他的要求很过分,但…我答应了,因为从我出生那天开始,我就发四要让每一个前端靓仔深深的爱上我,或许这就是该死的爱情。

    图片替换文本

    在办事之前我想先给家人们介绍一下我们穷士康的哥哥姐姐们。

    • 我大表哥【npm】: 给我拉活的
    • 我【webpack】: 穷士康工厂,打工是不可能打工的,这辈子不可能打工的,专门接点打包的单子做做,维持下生活这样子。我们工厂的宗旨是:帮助客户把一堆文件整合成另一堆好一点的文件
    • 厂长【Compiler】: 厂长,老伙计了,每次有单子下来都帮我协调的很好
    • 高级打工人【Compilation】:优秀打工人,很听厂长话,叫他干嘛就干嘛
    • 机动组工人【Plugin】:随传随到,很敬业。在干活的过程中,大家有事就打电话【hooks】叫他们来。他们有些是属于我们厂内部的【有内置插件】,也有第三方请来的【引用第三方插件】
    • 电话【hooks】:有很多个电话号码,能够打给不同的机动组工人【通过触发钩子,来调用插件执行】

    他们是我最爱的员工,个个都是人才!!!
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6XycNav5-1659080796322)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ef18b7a70d01460a8d3428cb01ca8574~tplv-k3u1fbpfcp-watermark.image?)]

    那开始吧,用力敲下npm run build。我便奋不顾身裸露在你面前。

    随着 npm run build回车执行,正题开始:

    如何叫我【webpack】开始干活 ?

    通常下面的代码可能手脚架搭建出来的项目都已经写好了,执行npm run build命令就执行了;但是呢,也有个别的靓仔喜欢特立独行,年轻人嘛!

    var webpack = require('webpack') 
    var config = require('./webpack.config')  // 自定义配置
    webpack(config, (err, stats) => {}) // 调用我我就干活
    
    • 1
    • 2
    • 3

    我会从下面三个地方结合生成最终的配置清单。【优先级也从高到低】

    • 命令行中【process.args读取 - Node的命令行参数解析】
    • 自定义配置
    • 默认配置

    接下来就进入了工厂的准备阶段【webpack()函数执行】

    工厂准备阶段【初始化阶段】

    合并配置

    • validateSchema大姐是我们厂的质检员,先帮我检查下客户的配置有没有出错。
    • new WebpackOptionsDefaulter().process(options)合并配置
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3QpQC7GJ-1659080796327)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e57bb912aa264c72b57a717c4940ce0d~tplv-k3u1fbpfcp-zoom-1.image)]
      tips:options可以是数组类型的,这里只按对象【即单个配置】的情况展开讨论

    叫厂长出来干活了【创建Compiler实例】

    我是老板,我只看不干!

    图片替换文本

    于是我把这次的整合好的任务清单【options】给到厂长【Compiler】

    compiler = new Compiler(options.context);
    
    • 1

    叫机动组的人来登记一下【注册插件】

    叫上工厂内部机动组人员【内置插件】以及外部机动组人员 【自定义配置引入的外部插件】来登记一下【注册插件】,以便干活的时候联系。

    例如:
    机动组人1号【SingleEntryPlugin插件】留了个电话make【钩子:make】。到时候我们打给make的时候,机动组人1号【SingleEntryPlugin插件】就能够接到电话然后干专属自己的活。当然可以多个人留同一个电话,到时候也是都能接听到。

    毕竟科技改变生活嘛,联想该学学了!

    // 注册配置文件的插件
    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);
    		}
    	}
    }
    // 注册内置插件
    // 内部插件之多,令人发指。这也进一步说明了我的设计真的很强,功能解耦,拓展性...令人敬佩。不愧是我
    // 此外还处理了devtool相关逻辑
    
    new WebpackOptionsApply().process(options, compiler);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    厂长下达命令开始打包【Compiler.run()/watch()】

    厂长这个时候出来喊话了:

    “comeon bady,everybody move!”

    【compiler.run()/compiler.watch() ,且都会再调用compiler.compile()】

    此话一出,各位打工人便开始忙碌起来!

    • 执行run的话,在打包结束后,厂长就回老家养老了,等我下次再有活他再开三轮车过来。
    • 但如果是watch(),厂长就要一直留在厂里【进程一直在进行】,客户一有需求马上再安排打包工作。【例如:热更新模式再次触发打包】
    • 但是打工人【Compilation】就不一样,到点就一定下班。这次的打包活干完铁定回家,从不996【只负责一次打包任务, 下一次打包再创建新的Compilation实例】。
    图片替换文本

    接着上图的代码
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3WCSCLVd-1659080796328)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0651e05ddccd4038b3c195e89086eb5d~tplv-k3u1fbpfcp-zoom-1.image)]

    最强打工人回来干活【实例化Compilation】

    贴一下compiler.js中的compile()函数
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-90Ffpfwc-1659080796355)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b17a8d52175d4771aec4fd6784e8599c~tplv-k3u1fbpfcp-zoom-1.image)]

    该函数做了两个重要的事:

    • 叫打工人【Compilation】开始干活【实例化Compilation】
    • 拨打号码make【触发钩子make】

    至此,我们称上面的工作为工厂的准备阶段【初始化阶段】,

    下面我们来总结下工厂准备阶段做了些什么。

    初始化阶段总结

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kPvLvuby-1659080796357)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4627c25d1fa34416a1bdabecd075d5d4~tplv-k3u1fbpfcp-zoom-1.image)]
    用厂里的话总结:

    • 老板我【webpack】整合好这次的任务清单【options】,
    • 然后叫厂长回来上班并且把任务清单给他【实例化Compiler】,
    • 厂长叫员工开始干活【compiler.run()/watch()】
    • 打工人回来干活【compiler.compile() => 实例化Compilation】
    • 厂长拨打电话“make”【触发make钩子】

    tips:过程中有钩子触发environment,afterEnvironment等,只说对我们重要的钩子

    tips:webpack中的钩子,有点类似于发布订阅模式,但更加的强耦合,因为这些订阅者能够影响打包的流程,发布者在通知订阅者时还把compiler、Compilation这些重要的构建上下文作为参数传递过去。这个事件机制是基于Tapable实现,非常值得学习的一种设计。

    到这里厂里的准备工作已经告一段落,厂长【Compiler】以及打工人【Compilation】已经就位【实例化】

    打工人干活阶段【构建阶段】

    还记得我们的宗旨吗?

    “帮助客户把一堆文件整合成另一堆好一点的文件”
    
    • 1

    打工人从入口开始【Compilation.addEntry()】

    在拨打“make”号码后,机动组SingleEntryPlugin员工接到电话,此时他不慌不忙的通知打工人【Compilation】从客户给的一堆文件中,根据任务清单【options】中找到入口,从这里开始整理客户提供的一大堆文件【Comilation.addEntry()】。

    所以说机动组员工登记要趁早【注册插件】,不然打电话找不到人就麻烦了。

    所以说员工按部就班,规规矩矩也是老板的福报。

    图片替换文本

    贴一下SingleEntryPlugin插件的代码:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fFdZLMzU-1659080796358)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4d6428bd117b42799d80b598b9dfbb51~tplv-k3u1fbpfcp-zoom-1.image)]

    总结一下:初始化阶段注册了SingleEntryPlugin插件,监听make钩子,监听到则触发compilation.addEntry()从入口文件开始打包。
    这个过程不简单,函数与函数之间基本都是通过callback回调,并且还有很多监听钩子的插件触发回调,所以这里需要很耐心很耐心才能够理解。

    厂言厂语总结:厂长【Compiler】打电话【hooks:make】,机动组SingleEntryPlugin接到电话,通知打工人【Compilation】从入口开始整理文件【Compilation.addEntry()】

    • tips:addEntry():该函数会接收一个Denpendency对象。后面然后根据Dependency创建Module。
    • tips:Dependency与Module的关系:先根据文件创建的Dependency,再创建Module里面又会解析文件内容创建新的Denpendency,新的Dependency又会创建Module…现有这么大概一个了解,接着往下看

    创建盒子【Compilation.handleModuleCreation()】

    新介绍一下厂里的材料:

    • 盒子【Module】:只装着一个文件的智能文件盒,最小单位了【这个盒子除了装着文件外,还会记录这个盒子依赖其他盒子的信息【盒子标签】】
    • 盒子标签【Dependency】:记录着一个文件地址,打工人【Compilation】会根据这个盒子标签【Dependency】创建智能文件盒【Module】,【盒子之间的关系也由盒子标签记录着】

    打工人【Compilation】在拿到入口的盒子标签后【Compilation.addEntry(dependency)】,会创建一个对应的盒子【Compilation.handleModuleCreation(dependency)】,紧接着Compilation会对这个盒子装着的文件进行解析,分析盒子有没有依赖其他的盒子,有的话就在这个盒子上面贴上这些盒子标签。

    想了很久,觉得还是有必要给你看看当时的情况。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rBMJuFWU-1659080796359)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f505f83d1d3d4a5293f5bb69736d5856~tplv-k3u1fbpfcp-watermark.image?)]

    这个盒子分析完之后呢,打工人会看看盒子上面有没有贴着盒子标签,有的话就又创建对应新的盒子了。

    接下来盒子是如何分析以及打上盒子标签的…

    解析文件【Loader + Parser】

    到这里我需要再给大家介绍下厂里的其他成员:

    • 加载机器【Loader】:厂里的机器设备,将客户的文件变成JavaScript
    • 解析员【Parser.js】:浸过咸水的高材生,将JavaScript通过acorn解析成AST,分析AST发现线索就打电话通知其他人【触发钩子】

    趁这次机会想给各位员工再培训一下,知己知彼,百战不殆!

    “各位员工好,大家都知道我们工厂服务对象是前端靓仔,而且我们的工厂是把前端靓仔提供的文件变成另一堆更合理的文件。”

    “那各位知道为什么我们是怎么做到的吗?"

    ”你们说得对,首先客户文件先转成JavaScript资源,我们配置了很多加载机器【Loader】,能够处理各种类型文件,变成Javascript“

    ”变成JavaScript之后呢?我们需要分析文件内容,这样我们才知道客户提供的文件哪些是要拆开的,哪些是要合并的,哪些是没用要去掉的,文件之前是怎么关联的。那怎么分析文件内容呢?“

    这个时候解析员走了出来说到:
    ”是AST,我用了AST! 我将JavaScript变成AST之后,去分析里面的(import/require)关键字,如果发现了就会打电话号码”exportImportSpecifier“【触发hooks:exportImportSpecifier】去通知机动人员HarmonyExportDependencyParserPlugin“,

    ”据我所知,这个机动组人员就是在盒子上打上盒子标签【Dependency】的那个人“。
    话音刚落,海归解析员嘴角止不住微微扬起,内心止不住想:还有谁?

    图片替换文本

    md,厂长【Compiler】忍不了别人装b,手里拿着两张工作流程图走了出来,里面有各个角色的分工以及流程。看完图纸我笑了,不愧是厂长,稳如老狗。还是我最信任的那个男人!

    图片替换文本
    这两个图纸要细看,厂里最值钱的东西了。
    
    • 1

    简易版:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AwoZhrI6-1659080796359)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f18252639a424d498b06c5aa92254d92~tplv-k3u1fbpfcp-zoom-1.image)]
    详细版:【这张图凝聚了我一个寂寞夜深的心血】
    一定要看这个图,细品

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Wtyaqnb-1659080796360)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a37acd62b0744ee691f1ff20a933da73~tplv-k3u1fbpfcp-zoom-1.image)]
    所以提个问题?

    顺藤摸瓜【Dependency -> Module】

    ”入口文件index.js内导入了a.js,那我们工厂是怎么知道他们是有这样的引用关系的呢?打工人【Compilatin】,你站起来说一下“

    打工人【Compilatin】也站了起来,忍不了高材生装b的看来不止厂长一个,说道:

    • 首先我【Compilation】会读取配置的入口信息,找到入口文件index.js装在一个文件盒【module】。
    • 标记1紧接着把这个文件盒【module】放到加载机器【Loader】转化成的Javascript,Javascript也同样放在文件盒内。
    • 这个时候解析员【Parser.js】接收了文件盒【module】,把盒子里的Javascript通过acorn转化成AST对象
    • 解析原【Parser.js】对这个AST进行分析,也可以说是对这个文件的内容进行分析,如果发现【require/import】这样的关键字,就说明这个文件跟其他文件存在关联,准确来说是这个文件依赖其他文件。
    • 紧接着解析员拨打电话号码”exportImportSpecifier“要通知机动人员【触发hooks:exportImportSpecifier】,当然解析员也不知道接电话的是谁,总之打电话,说明文件关联的事情就完事。真正的高效就是每个人职责分明,分工明确。
    • 这个时候机动组工人HarmonyExportDependencyParserPlugin【HarmonyExportDependencyParserPlugin插件】接到电话”exportImportSpecifier“,然后给这个文件盒【module】加上依赖信息dependencies,说明需要依赖哪个文件【调用module.addDependency()】
    • 这个时候我检查一下这个文件盒【module】的dependencies依赖信息,发现依赖信息记录着a.js,此时利用依赖创建一个新的文件盒,把文件装进去【创建module对象】, 又从标记1开始处理这个文件盒,直到客户递归完所有的文件盒子。
    • 好了,最后你们自己好好想想这个文件盒里面除了文件还有什么信息。
      "

    说完,全场掌声雷动,精彩的演讲!堪称前端特冷普。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nF05XN2A-1659080796361)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f67c5a44041d4fd180e09b6a6e420f55~tplv-k3u1fbpfcp-watermark.image?)]

    那么你如何理解module顺藤摸瓜的过程?

    • 1、make钩子发布,SingleEntryPlugin监听到调用Compilation.addEntry()。从第一个入口module开始
    • 2、module通过loader转JavaScript再由acorn转AST后
    • 3、分析语句import/require等依赖语句,生成dependency对象,通过依赖对象生成新的module
    • 4、新的module再从2开始循环,直到不能找到其他依赖

    构建过程总结

    从对象的角度

    compilation

    • addEntry()找到入口文件开始,
    • 不同文件类型创建不同的module子类
    • 调用module.build()开始构建
    • 遍历module的依赖列表,【遍历依赖列表再重复构建过程】

    module

    • 从module.build()开始
    • 调用runLoader将文件转成Javascript资源
    • Javascript资源再由Parser对象使用acorn转成AST【parser.parse()】
    • 解析AST的过程触发解析语句相关钩子
    • HarmonyExportDependencyParserPlugin插件监听到AST解析钩子exportImportSpecifier则会回调module.addDependency()将依赖对象添加到module的依赖列表中
    • handleParseResult()执行后执行回到compilation执行【compilation遍历module的依赖列表】

    Parser

    • 从module内调用parser.parse()开始
    • 利用acorn将Javascript转成AST对象
    • 分析AST【prewalkStatements()、walkStatements()】,触发钩子exportImportSpecifier
    • HarmonyExportDependencyParserPlugin监听到钩子,则调用module.addDependency()
    • 回到module执行

    从文件的角度

    例如以下文件结构,index.js作为入口

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-09q746Wq-1659080796361)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/33d6b090f4334abe9ebf6fb9dea92e5d~tplv-k3u1fbpfcp-zoom-1.image)]

    此时通过Compilation.addEntry()找到index.js,导入成module对象,同时分析得出module的依赖列表有left.js以及right.js

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8C9Prpjd-1659080796362)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1eab9183294449fb483b0718205cb6d~tplv-k3u1fbpfcp-zoom-1.image)]

    此时遍历依赖列表[dependency-left.js, dependency-right.js],创建对应的module-left.js、module-right.js

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eVUqxGrD-1659080796363)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d1375f461edc4ef3b7bf32798bb00f0c~tplv-k3u1fbpfcp-zoom-1.image)]

    再遍历下一批依赖

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSk2GdVb-1659080796363)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/47e06c222d424e8f96d81afdc899d88f~tplv-k3u1fbpfcp-zoom-1.image)]

    再看下完整的文件构建流程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9vNsR9Yv-1659080796364)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dd17d48f27ef480c9f86ccaa03e3a34e~tplv-k3u1fbpfcp-zoom-1.image)]

    至此构建阶段告一段落。在文件盒都准备好之后,则进入下一步

    装箱交货阶段【生成阶段】

    先思考一个问题:我们为什么要装箱?

    如果将我们的开发文件1比1的打包到生产环境上的话,数量上是非常大的,这会导致浏览器发起的http请求资源次数则需要非常频繁,可能一个module就一两行代码,我们依然要发起一个http去请求它回来。而浏览器对同时能发起的http数量是有限制的,例如chrome就是最多同时6个请求,所以最后放到的生产的文件不能太多,不能太小,也不能太大。我们有时候看到的各种打包优化策略,也是朝着这个目标尽量实现。
    所以我们需要将他们合并组成一个一个块,以达到目的。

    这里我们再介绍一个我们的厂的成员:

    • 箱子【chunk】:我们厂拿来装文件盒【module】用的,就是你们平常见的纸皮箱,没什么特别。但是,那么多文件盒,把他们合理的分配在不同箱子就是门技术活了。

    在上面阶段整理出来那么多的文件盒【module】,我们会先把他们装箱【chunk】再交付客户【输出文件】。

    装箱【compilation.seal()】

    这些箱子里面装的都是module

    图片替换文本

    在递归生成所有的文件盒【module】之后,打工人【Compilation】就开始装箱操作了【compilation.seal()】【module分配到chunk】。

    其实我也一直疑惑,打工人【Compilation】是怎么装箱的,例如要使用到多少个箱子?哪些文件盒放到哪些箱子?这其中必然有功夫。

    于是我以老板的身份要求打工人说出自己的秘籍!

    打工人【Compilation】开始巴拉巴拉:“除非升职加薪,否则不说!”

    我:“最近公司准备优…”,话还没说完,

    打工人准备全盘托出:

    图片替换文本

    “首先我自己有一套默认装箱的规则”

    • 一个入口能关联到的所有文件盒【module】放到一个箱子【chunk】
    • 动态依赖的文件盒【例如import()这样的语法】,放到一个箱子【chunk】

    动态依赖的分包规则补充一个点:
    例如:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X8qjawBW-1659080796365)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f7ce5e1bdafd44ab8743d5970de600a2~tplv-k3u1fbpfcp-zoom-1.image)]

    那么一个箱子装【index.js+right.js】,另一个箱子装【left.js + left-1.js + left-2.js】.而不是另一个箱子只装【left.js】喔!

    装箱逻辑的实现在不同的webpack版本中差异还是比较大的:

    〇 webpack4的时候还是相对简单一点点,装箱逻辑直接利用module以及dependency之间相互引用的信息,

    〇 webpack5则做了升级【将module与dependency之间的依赖关系拆分的更加细致】:

    增加了下面这些对象

    • ChunkGraph:所有关于模块如何与块连接的信息现在都存储在 ChunkGraph 类中

    • ModuleGraph:所有关于模块如何在模块图中连接的信息现在都存储在 ModuleGraph 类中。

    • ModuleGraphConnection:记录模块间的引用以及被引用的关系;originModule字段记录被引用的module,module字段表示自己

    • ModuleGraphModule:incomingConnections字段记录自己的ModuleGraphConnection,outgoingConnections字段记录依赖的module的ModuleGraphConnection

      webpack5正是将module与depenency的关系拆分的如此细致,加上在seal()阶段做了更多的优化,webpack5在打包性能上面又进步了不少。
      例如:

      • 优化TreeShaking
      • 增加基于文件系统的的持久化缓存,对于多次构建提升非常明显。而以前只有基于内存的持久化缓存,在增量构建的时候略显鸡肋,性能与时间不能同时兼顾。

    这里再介绍两位帮助优化装箱的工厂成员:

    • 机动组工人罗老师【SplitChunksPlugin】:webpack>=4时用,大家叫他装箱【分包】管理大师,很会管理,所以大家叫他罗老师,由于太出色,招进来当我们webpack工厂内部人员了【内置插件】
    • 机动组工人王宝强【SplitChunksPlugin】:webpack<4时用,老实憨厚的老一辈【分包】大师【第三方插件】,没啥好说的,一杯敬乃亮,一杯敬宝强
    图片替换文本

    打工人【Compilation】把老底都说出来时候,眼泪忍不住从眼角流出,向生活低下了高贵的头颅。

    打工人【Compilatin】继续说道:

    “除了内置的装箱规则,客户也可以点名叫机动组的人帮他们分包【插件-上面提到的两位大师】”

    语气里多少都带点小脾气。

    “还有就是机动组员【SplitChunksPlugin】会在我装箱之后,再偷偷摸摸再搞一遍装箱,客户自定义的分包规则就是在这里起作用的,当然客户不自定义,罗老师也有自己的一套默认规则”

    罗老师的默认配置

    module.exports = {
      
      
      optimization: {
        //罗老师的默认规则,webpack4跟webpack5默认好像不一样
        splitChunks: {
          chunks: 'async', // 2. 处理的 chunk 类型
          minSize: 20000, // 4. 允许新拆出 chunk 的最小体积
          minRemainingSize: 0,
          minChunks: 1, // 5. 拆分前被 chunk 公用的最小次数
          maxAsyncRequests: 30, // 7. 每个异步加载模块最多能被拆分的数量
          maxInitialRequests: 30, // 6. 每个入口和它的同步依赖最多能被拆分的数量
          enforceSizeThreshold: 50000, // 8. 强制执行拆分的体积阈值并忽略其他限制
          cacheGroups: { // 1. 缓存组
            defaultVendors: {
              test: /[\\/]node_modules[\\/]/, // 1.1 模块路径/文件名匹配正则
              priority: -10, // 1.2 缓存组权重
              reuseExistingChunk: true, // 1.3 复用已被拆出的依赖模块,而不是继续包含在该组一起生成
            },
            default: {
              minChunks: 2, // 5. default 组的模块必须至少被 2 个 chunk 共用 (本次分割前) 
              priority: -20,
              reuseExistingChunk: true,
            },
          },
        },
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    交付客户【输出文件】

    这里没啥好说的,打工人【Compilation】装箱之后【compilation.seal()完成】,厂长【Compiler】就把箱子给到客户了【compiler.emiAssets()将chunk生成一个个文件】

    结尾

    就这样日复一日,年复一年,我们厂为所有一线奋斗的客户解决模块化打包的问题。渐渐的有些客户想了解我们工厂内部的运作原理,为此我作为工厂老板,势必要为我们穷士康写下这么一篇文章,让更多的人能够了解我们。但苦于才疏学浅,文笔功力有限;且厂里部门繁多,各种人情世故暂时也只能写下这么一篇水平有限的文章。

    不到之处望海涵~









    看到文章的人今年必定升值加薪!

    点个赞吧👍【肮脏手段:不点赞不是中国人】

  • 相关阅读:
    Mysql分页、SSM项目分页实战
    python之格式化输出format()函数使用总结
    四种主流的prompt框架
    Java 并发编程解析 | 如何正确理解Java领域中的锁机制,我们一般需要掌握哪些理论知识?
    Spring Cloud Alibaba Sentinel 简单使用
    Java项目论文+PPT+源码等]S2SH+mysql水费管理系统
    JSP webshell免杀——JSP的基础
    计算机毕业设计(附源码)python知识付费运营管理系统
    <老式喜剧>
    设计高并发系统的关键策略
  • 原文地址:https://blog.csdn.net/bigname22/article/details/125977476