coolie 能做的:JS 模块依赖分析、合并、压缩、版本管理
一、引子
假设现在要做一台计算机,coolie 是帮你这样做到的:
- 根据你的要求,从全世界购买各种零件,包括 CPU、主板、网卡等。
- 将这些零件封装成一台看起来比较丑陋的计算机原型。
- 将多余的电线剪掉、去除多余的零件,加上包装盒。
coolie 就这么漂亮的完成了你分配给它的任务,任劳任怨。
本文将从 JS 模块依赖分析、合并、压缩、版本管理开始讲讲 coolie 到底能做什么,值得作者这么牛气哄哄的推荐他。我想前端 JS 目前需要做的事情也就这些了吧,这也是我为什么不遗余力的推荐的一个理由。
二、模块依赖分析
描述以下场景:你的网站就一个页面,一个首页,首页上有一个 index.js。但是你的首页比较复杂,index.js 使用了各种脚本库、插件,包含关系是这样的:
- index.js
|-- jquery.js
| `-- jquery.fullscreen.js
| `-- fullscreen.js
`-- reamd.md
图示,不表示层级关系,仅表示引用关系,其实 5 个文件是同级的。coolie 是如何知道这个依赖分析的?当然你必须写一些依赖标记,让 coolie 知道你的依赖关系。因此,你的 index.js 应该是这么写的(为了更好的描述,define 及其他代码被省略了):
// index.js
require('./fullscreen.js');
index.js 作用很简单,他只需要 fullscreen.js。index.js 不需要关心 fullscreen.js 是如何实现的,你爱怎么来怎么来。但是 fullscreen.js 给我的接口一定是可用的。就像一开始说的组装电脑,coolie 去购买 CPU,而不会关心 CPU 是如何做出来的,只需要能用即可。当然,这里只讨论实现,不能较真,如果你非要说,不知道 fullscreen.js 可能吗?
与 index.js 道理一样,fullscreen.js 也是如此。fullscreen.js 只需要 jquery.fullscreen.js。
// fullscreen.js
require('./jquery-fullscreen.js');
jquery.fullscreen.js 也是如此,终点文件是 jquery.js ,他没有任何依赖。因此,coolie 可以根据源代码上的require
关键字来找到他们之间的依赖关系,因此require
关键字不能被修改,这是一个小约定,开发者和 coolie 的小约定。他们之间的依赖关系最终被分析出来是这样的:
index.js(入口文件) => fullscreen.js => jquery.fullscreen.js => jquery.js
三、模块合并
coolie 在上一步已经将入口文件的依赖关系分析出来了,coolie 很聪明,在分析依赖的时候就已经将模块合并起来了,并且保存在内存里,等待处理。合并后的模块集合是这样的:
// index.js
define(function(require, exports, module){require('./fullscreen.js')});
// fullscreen.js
define(function(require, exports, module){require('./jquery-fullscreen.js')});
// jquery-fullscreen.js
define(function(require, exports, module){require('./jquery.js')});
// jquery.js
define(function(require, exports, module){});
四、模块压缩
coolie 在上一步的得到模块集合的基础上,会将模块进行压缩。压缩之后只有一个新文件了,并且模块的路径也已经被压缩成 1 个字符了,所以 coolie 不需要任何的路径别名配置,require 进来就好了。
// index.abcdef123456.js
define('0', ['1'], function(a, b, c){a('1')});
define('1', ['2'], function(a, b, c){a('2')});
define('2', ['3'], function(a, b, c){a('3')});
define('3', [], function(a, b, c){});
上面的代码,人类已经无法正常阅读了,这是给机器读的。通过上面的关系,我们可以到这样的依赖关系:
0(入口模块) => 1 => 2 => 3
那么模块的对应关系是:
0 <=> index.js(入口模块)
1 <=> fullscreen.js
2 <=> jquery.fullscreen.js
3 <=> jquery.js
五、自动入口版本管理
可能你已经注意到,coolie 处理过的 index.js,名称变为了index.abcdef123456.js
,其中index
是初始名字,coolie 会保留他,因为这是入口模块,是硬编码。而后面的abcdef123456
是版本号,文件名后面加一串随机字符串来表示版本号,是不是觉得自己的逼格顿时变高了?
那么,coolie 是怎么进行版本管理的,还是以 index.js 为例。
- index.js
|-- fullscreen.js
|-- jquery.fullscreen.js
`-- jquery.js
coolie 很聪明的,在分析依赖这一步就进行文件 MD5 计算了:
- index.js => md5: abc
|-- fullscreen.js => md5: def
|-- jquery.fullscreen.js => md5: 123
`-- jquery.js => md5: 456
coolie 将依赖链里的各个文件的 MD5 值计算出来,然后安装组装顺序合并起来。因此,只要依赖链的任何一个模块发生了变化,那么这个文件的最终产出就会发生变化,这就是 coolie 能够进行 JS 模块版本化管理的奥秘。
六、总结
coolie 很渺小,要做的事情还有很多,比如生成路径对应关系、sourceMap,动态合并等等。他要走的路还很长,但他能做的已经完全能够解决当前前端开发中的一些重点和难点。
市面上能够和 coolie 一样做的一样好的,可能还有(?),coolie 抱着开放的态度,吸收好的,摒弃坏的。
那我们为什么还要选择 coolie?
coolie 能做的很简单:一条命令,发布一个工程;少量配置,分分钟上手。coolie 维护了是前端工程化的一环:前端开发构建,做的纯粹、足够。coolie 不会增加配置,来适配各种特殊情况,一定的约定可以让 coolie 保持最快的速度。下面简要的做个对比:
构建工具 | JS版本管理 | JS构建配置 | JS 模块路径压缩 |
---|---|---|---|
百度 fis | 半自动 | 复杂、繁杂 | 无法 |
淘宝 spm | 手动 | 繁杂 | 无法 |
webpack | 手动 | 简单 | 可以 |
coolie | 全自动 | 少量配置 | 可以 |
简单的比较之后,你该怎么选择。
coolie 能做的,不止如此,还有更多,后文详述。