前端工程师手册

模块依赖管理工具对比

CommonJS

2009 ~ 2010 年间,CommonJS 社区大牛云集,稍微了解点历史的同学都清楚,在同时间出现了 nodejs,一下子让 javaScript 摇身一变,有了新的用武之地,同时在nodejs推动下的 CommonJS 模块系统也是逐渐深入人心:

  • 通过 require 就可以引入一个 module,一个module通过 exports 来导出对外暴露的属性接口,在一个module里面没有通过 exports 暴露出来的变量都是相对于module私有的
  • module 的查找也有一定的策略,通过统一的 package.json 来进行 module 的依赖关系配置,require一个module只需要require package.json里面定义的name即可

这样的实现确实很好,但是一个最大的问题就是在浏览器加载脚本天生不支持同步的加载,无法通过文件I/O同步的require加载一个js脚本。So what ? CommonJS 中逐渐分裂出了 AMD,这个在浏览器环境有很好支持的module规范,其中最有代表性的实现则是 requirejs。

AMD

The Asynchronous Module Definition (AMD) API specifies a mechanism for defining modules such that the module and its dependencies can be asynchfanronously loaded. This is particularly well suited for the browser environment where synchronous loading of modules incurs performance, usability, debugging, and cross-domain access problems.

翻译过来就是说:异步模块规范 API 定义了一种模块机制,这种机制下,模块和它的依赖可以异步的加载。这个非常适合于浏览器环境,因为同步的加载模块会对性能,可用性,debug调试,跨域访问产生问题。

确实,在浏览器环境下,AMD有着自己独特的优势: 由于源码和浏览器加载的一致,所见即所得,代码编写和debug非常方便。尤其是在多页面的web项目下,不同页面的脚本js都是根据依赖关系异步按需加载的,不用手动处理每个页面加载js脚本的情况。

但是,AMD 有一个不得不承认的作为一个module system的不足之处。请问?在 AMD(requireJS)里面怎么使用一个第三方库的?一般都会经历这么几个步骤:

  • 使用的第三方库不想成为 global 的,只有引用的地方才可见
  • 需要的库支不支持 AMD ?
  • 不支持 AMD,我需要 fork 提个 patch 吗?
  • 支持AMD,我的项目根路径在哪儿?库在哪儿?
  • 不想要使用库的全部,要不要配置个 shim?
  • 需不需要配置个 alias ?

一个库就需要问这么些个问题,而且都是人工手动的操作。最最关键的问题是你辛辛苦苦搞定的配置项都是相对于你当前项目的。当你想用在其他项目或者是单元测试,那么OK,你还得修改一下。因为,你相对的是当前项目的根路径,一旦根路径发生改变,一切都发生了变化。

requireJS 使用之前必须配置,同时该配置很难重用.

CommonJS in browser

相比较于 CommonJS 里面如果要使用一个第三方库的话,仅仅只需要在 package.json 里面配置一下 库名和版本号,然后npm install一下之后就可以直接 require 使用的方式,AMD 的处理简直弱爆了 !!!

对于 AMD 的这个不足之处,又有社区大神提出了可以在 browser 运行的 CommonJS 的方式,并且通过模块定义配置文件,可以很好的进行模块复用。比较知名的就有 substack 的 browserify, tj 曾主导的 component,还有后来的 duo,webpack,时代就转眼进入了 browser 上的 CommonJS.

由于 CommonJS 的 require 是同步的,在 require 处需要阻塞,这个在浏览器上并没有很好的支持(浏览器只能异步加载脚本,并没有同步的文件I/O),CommonJS 要在 browser 上直接使用则必须有一个 build 的过程,在这个 build 的过程里进行依赖关系的解析与做好映射。这里有一个典型的实现就是 substack 的 browserify。

browserify

browserify 在 github 上的 README.md 解释是:

require('modules') in the browser Use a node-style require() to organize your browser code and load modules installed by npm. browserify will recursively analyze all the require() calls in your app in order to build a bundle you can serve up to the browser in a single