Open youngwind opened 7 years ago
本来 chunk1 应该是包含模块 c、b 和 d 的,但是由于 c 已经被其 parent-chunk(也就是 chunk1)包含,所以,必须将 c 从 chunk1 中移除,这样方能避免代码的冗余。 chunk2(2.output.js)是从 chunk0 中切割出来的,所以 chunk0 也是 chunk2 的 parent。 chunk2 包含 e 和 f 两个模块。 `` // example.js var a = require("a"); var b = require("b"); a(); require.ensure(["c"], function(require) { require("b")(); var d = require("d"); var c = require('c'); c(); d(); });
require.ensure(['e'], function (require) { require('f')(); }); `` 在上面的example.js里,同样是require.ensure(["c"]) require.ensure(["e"]) , 为什么c模块包含在parent-chunk里,e模块却没包含在parent-chunk里? 如果因为c在主chunk里,所以chunk1里会把c移除,那为什么chunk2里不把e移除?为什么chunk2里会包含e呢?为什么c可以从chunk1移除,e却没从chunk2移除呢? 实际打包出来的结果是:chunk1包含c,d chunk2包含e,f 并不是你说的c从chunk1移除阿
@huangzhiyang166 抱歉,此处是我笔误了,多谢指正。需要移除的是模块 b,而非模块 c,2是模块 b 的 id,如文中示意图所示。
也就是说 t0 是其他t的父亲, 呵呵 。。呵呵
t0有的 其他t就不能有了, t0 t1 t10, t10
我也分不清module和chunk的区别,以后读源码还有一个方法就是像作者一样将功能最小化。写一个简单的例子再结合分析的重点就大致看出源码的走向。
mark
前言
继上一篇 #99 之后,我们今天来看看如何实现 webpack 的代码切割(code-splitting)功能,最后实现的代码版本请参考这里。至于什么是 code-splitting ,为什么要使用它,请直接参考官方文档。
目标
一般说来,code-splitting 有两种含义:
换句话说,我们的目标是:将原先集中到一个 output.js 中的代码,切割成若干个 js 文件,然后分别进行加载。 也就是说:原先只加载 output.js ,现在把代码分割到3个文件中,先加载 output.js ,然后 output.js 又会自动加载 1.output.js 和 2.output.js 。
切割点的选择
既然要将一份代码切割成若干份代码,总得有个切割点的标志吧,从哪儿开始切呢? 答案:webpack 使用
require.ensure
作为切割点。然而,我用 nodeJS 也挺长时间了,怎么不知道还有
require.ensure
这种用法?而事实上 nodeJS 也是不支持的,这个问题我在CommonJS 的标准中找到了答案:虽然 CommonJS 通俗地讲是一个同步模块加载规范,但是其中是包含异步加载相关内容的。只不过这条内容只停留在 PROPOSAL (建议)阶段,并未最终进入标准,所以 nodeJS 没有实现它也就不奇怪了。只不过 webpack 恰好利用了这个作为代码的切割点。ok,现在我们已经明白了为什么要选择
require.ensure
作为切割点了。接下来的问题是:如何根据切割点对代码进行切割? 下面举个例子。例子
假设这个 example.js 就是项目的主入口文件,模块 a ~ f 是简简单单的模块(既没有进一步的依赖,也不包含
require.ensure
)。那么,这里一共有2个切割点,这份代码将被切割为3部分。也就说,到时候会产生3个文件:output.js ,1.output.js ,2.output.js识别与处理切割点
程序如何识别
require.ensure
呢?答案自然是继续使用强大的 esprima 。关键代码如下:观察上面的代码可以看出,识别出![image](https://cloud.githubusercontent.com/assets/8401872/22817044/152b9d9a-efa0-11e6-9f68-03ab2cee876d.png)
require.ensure
之后,会将其存储到 asyncs 数组中,且继续遍历其中所包含的其他依赖。举个例子,example.js 模块最终解析出来的数据结构如下图所示:module 与 chunk
我在刚刚使用 webpack 的时候,是分不清这两个概念的。现在我可以说:“在上面的例子中,有3个 chunk,分别对应 output.js、1.output.js 、2.output.js;有7个 module,分别是 example 和 a ~ f。
所以,module 和 chunk 之间的关系是:1个 chunk 可以包含若干个 module。 观察上面的例子,得出以下结论:
好了,下面进入重头戏。
构建 chunks
在对各个模块进行解析之后,我们能大概得到以下这样结构的 depTree。![image](https://cloud.githubusercontent.com/assets/8401872/22817513/ae8cb94a-efa2-11e6-891c-ad10efd32839.png)
下面我们要做的就是:如何从8个 module 中构建出3个 chunk 出来。 这里的代码较长,我就不贴出来了,想看的到这里的 buildDep.js 。
其中要重点注意是:前文说到,为了避免代码的冗余,需要将模块 b 从 chunk1 中移除,具体发挥作用的就是函数![image](https://cloud.githubusercontent.com/assets/8401872/22817860/cb236a70-efa4-11e6-88db-85fd3591cd24.png)
removeParentsModules
,本质上无非就是改变一下标志位。最终生成的chunks的结构如下:拼接 output.js
经历重重难关,我们终于来到了最后一步:如何根据构建出来的 chunks 拼接出若干个 output.js 呢? 此处的拼接与上一篇最后提到的拼接大同小异,主要不同点有以下2个:
后话
其实关于 webpack 的代码切割还有很多值得研究的地方。比如本文我们实现的例子仅仅是将1个文件切割成3个,并未就其加载时机进行控制。比如说,如何支持在单页面应用切换 router 的时候再加载特定的 x.output.js?
-------- EOF -----------