commonJS
在我对js的理解还停留在只用来做做页面认证和特效的时候,它早已牛逼到支持操作系统层面的操作了。根据学习整理,大致梳理出这么一个故事 —— 虽然js天生的弱类型和半吊子面向对象的特性在逐步向强类型语言靠拢,但js没有模块系统、标准库较少、没有包管理工具的痛点阻碍着js发展出开发大型应用的能力。于是commonJS跳出来尝试解决这个问题。commonJS的首页上就写着
javascript: not just for browsers any more !
可见commonJS是希望Js运行在浏览器以外发挥作用的。
commonJS针对JS模块化开发制定了一系列规范,主要包括模块引用、模块定义、模块标识。规范仅仅是规范,亦可以说是指导,而node.js实现了它。nodejs把Chrome V8从浏览器中剥离出来,又编写了一部分底层代码,添加了对I/O操作的支持,js获得了在操作系统层面的运行能力,于是浏览器框不住js了。
在commonJS中,一个文件就是一个模块,拥有单独的作用域, 普通方式定义的变量、函数、对象都属于该模块内,因为所有代码都运行在局部作用域内,因此避免了污染全局作用域的问题。模块可以多次加载但只会运行一次,此后模块被缓存起来可以被直接调用,模块的加载顺序由书写顺序决定,以同步的方式加载。
但commonJS旨在使js获得在服务端开发的能力,因此模块规范的订制都采用同步的方式,但在浏览器中,同步意味着用户必须等待程序的执行、加载等步骤,由此造成严重的用户体验和使用性能问题。那么浏览器中js程序的模块化规范怎么办呢?
AMD和CMD
实际上,为了达到浏览器端模块化开发的目的,目前有两种常用的规范——AMD(Asynchronous Module Definition)和CMD(Common Module Definition )。引用玉伯大佬在知乎上的解释。
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
AMD和CMD均实现了异步模块加载,但有其各自的特点。
AMD
RequireJS应用的异步加载模块规范,需要注意的是,依赖必须在模块开头写明。AMD主要提供define和require两个方法来进行模块化编程 ,define用于定义模块,require用于引入模块。
AMD的define
define用于定义模块,通过return对象来暴露内部定义的属性和方法,实际上应用的就是采用 匿名函数模块化开发 的思路。关于基本js模块化开发的介绍可移步另一篇博客 —— 关于javascript模块化开发
1 | define(function(){ |
当模块独立运行不需要依赖别的模块时,上面的代码是可行的。当模块之间存在依赖时,需要在define中以数组的方式指明依赖的模块。
1 | define(['./modules1','./modules2',..],function(m1,m2,...){ |
可以看出,在添加了依赖的模块后,define的回调函数中指定了几个形参,这几个形参即为各模块中返回的对象,因此可以通过调用形参的方法获得别的模块中定义的功能。
AMD的require
require用于引入模块,语法也与define相近,处理亦置于回调函数中。
1 | require(['./modules1','./modules2',..],function(m1,m2,..){ |
AMD的调用方式
RequireJS的调用采取标签的形式,标签中指定入口。
1 | <script data-main='./scripts/main' src='./scripts/require.js'></script> |
data-main指定了模块的入口,这里main.js即为入口,依赖模块将通过入口开始异步加载进入执行。
CMD
这是淘宝的攻城狮玉伯开发的seaJS执行的标准,名字据说是有“ 海纳百川,有容乃大 ”的寓意。CMD和AMD都有define和require方法,但CMD实际上是不需要在require和define中指定依赖的。
CMD的define
define的三个参数分别指明
require – 依赖的模块(以id为标识符)(推荐就近书写)
exports – 向外界提供的接口
modules – 一个对象,存储当前模块的属性和方法【id(标识符),uri(绝对路径),exports(导出接口)】
1 | //CMD |
CMD的调用方式
首先是毫无疑问的第一步。。引入seaJS。
1 | <script src="../sea-modules/seajs/seajs/2.2.0/sea.js"></script> |
在引入seaJS后,进行基础的配置。
1 | // seajs 的简单配置 |
当然seaJS的配置还有很多。。有个博客写的很详细,传送门。
CMD和AMD的区别
至于AMD和CMD的差别,引用玉伯的描述:AMD先定义所有依赖,操作在加载完成后的回调函数中执行,而CMD依赖就近,用到什么依赖到时候再说。对于依赖的模块,AMD提前执行,CMD延后执行。(但执行AMD标准的RequireJS也已添加延迟执行的选项。)另外,AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。
CMD的哲学是 as lazy as possible,emm深得我心。。