一、引子

假设现在要做一台计算机,coolie 是帮你这样做到的:

  1. 根据你的要求,从全世界购买各种零件,包括 CPU、主板、网卡等。
  2. 将这些零件封装成一台看起来比较丑陋的计算机原型。
  3. 将多余的电线剪掉、去除多余的零件,加上包装盒。

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 能做的,不止如此,还有更多,后文详述。