AMD vs. CommonJS?

js开发者对js模块加载的尝试和创新历来都没有中止过,尤为是当nodejs的出现后,模块化加载的必要性更加凸显。本文不讨论如何在nodejs环境来模块化加载(创造者已经利用commonJS机制解决),只讨论在浏览器环境下如何来模块加载的思路,并提出一些个人见解。javascript

浏览器环境与nodejs的环境的最大差别是,对于nodejs的环境,大多数状况下被依赖的模块文件自己就在本地(它们都在服务器上),同步取过来就能用;而对于浏览器的环境,被依赖的模块文件一般还在远程服务器上,并未加载到本地,也就是说必须是先加载(并解析)后执行的机制。java

既然已经有了commonJS,在这之上将异步回调的逻辑加入进去,但是异步先加载什么呢?因而就有了依赖的概念。一段时间的发展后,有了AMD、和CMD的解决方案,表明做品是requirejs和seajs,有兴趣的读者能够去了解一下,这里就不展开介绍了。
有必要简单提一下二者的主要区别,CMD推崇依赖就近,能够把依赖写进你的代码中的任意一行,例:node

1
2
3
4
5
6
define( function (require, exports, module) {
   var  a = require( './a' )
   a.doSomething()
   var  b = require( './b' )
   b.doSomething()
})

代码在运行时,首先是不知道依赖的,须要遍历全部的require关键字,找出后面的依赖。具体作法是将function toString后,用正则匹配出require关键字后面的依赖。显然,这是一种牺牲性能来换取更多开发便利的方法。数组

而AMD是依赖前置的,换句话说,在解析和执行当前模块以前,模块做者必须指明当前模块所依赖的模块,表如今require函数的调用结构上为:浏览器

1
2
3
4
define([ './a' , './b' ], function (a,b){
    a.doSomething()
    b.doSomething()
})

代码在一旦运行到此处,能当即知晓依赖。而无需遍历整个函数体找到它的依赖,所以性能有所提高,缺点就是开发者必须显式得指明依赖——这会使得开发工做量变大,好比:当你写到函数体内部几百上千行的时候,突然发现须要增长一个依赖,你不得不回到函数顶端来将这个依赖添加进数组。服务器

细心的读者可能发现,到目前位置我讨论的AMD和CMD的思想的关于依赖的部分,都只讨论的“硬依赖”,也就是执行前确定须要的依赖,可是这不是所有的状况。有的时候状况是这样的:异步

1
2
3
4
// 函数体内:
if (status){
   a.doSomething()
}

在这个函数体内,可能依赖a,也可能不依赖a,我把这种可能的依赖成为“软依赖”。对于软依赖固然能够直接当硬依赖处理,可是这样不经济,由于依赖是不必定的,有可能加载了此处的依赖而实际上没有用上。
对于软依赖的处理,我推荐依赖前置+回调函数的实现形式。上面的例子简单表述以下:async

1
2
3
4
5
6
// 函数体内:
if (status){
   async([ 'a' ], function (a){
     a.doSomething()
   })
}

至此能够对由commonJS衍生出来的方案作出总结了。在浏览器端来设计模块加载机制,须要考虑依赖的问题。
咱们先把依赖分为两种,“强依赖” —— 确定须要 和“弱依赖” —— 可能须要。
对于强依赖,若是要性能优先,则考虑参照依赖前置的思想设计你的模块加载器,我我的也更推崇这个方案一些;若是考虑开发成本优先,则考虑按照依赖就近的思想设计你的模块加载器。
对于弱依赖,只须要将弱依赖的部分改写到回调函数内便可。
若是如今我要实现一个模块加载器,我会将强依赖前置,弱依赖采用异步回调函数的形式,其它的方法我认为都只是语法糖而已,仅此就够了。模块化

相关文章
相关标签/搜索