目录
1. 什么是模块化?
2. 无模块化时代
3. 传统模块化阶段
3.1. “对象”型模块
3.2. “仿Java类”型模块
3.3. “立即执行函数(IIFE)”型模块
3.4. “全局变量输入”型模块
4. 百家争鸣:CommonJS、AMD、CMD
4.1. CommonJS
4.2. AMD
4.3. CMD
5. 一统天下:ES6 Module
1. 什么是模块化?
模块化开发就是封装细节,提供使用接口,彼此之间互不影响,每个模块都是实现某一特定的功能。
——《软件工程》
在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。
每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。
——《Webpack官网》
有啥好处?
a. 避免命名冲突(不占全局命名空间);
b. 便于依赖管理(无须手动组织JS文件顺序);
c. 利于性能优化(异步模块加载);
e. 提高可维护性;
f. 利于代码复用;
2. 无模块化时代
最初,大家只是把项目中的功能,以文件为单位进行划分;这么干的结果是.....所有的变量、函数都暴露在全局作用域;多人协作开发时,极易出现命名冲突,也容易为了避免命名冲突,硬造一些稀奇古怪的名字....时间越长,麻烦越多...维护成本也越高...
3. 传统模块化阶段
这一阶段,WEB 开发人员主要是利用 JS 语言的闭包、原型、函数作用域等特性,减少对全局命名空间的污染;方式方法各有不同,但结果都差不多,比较混乱...
3.1. “对象”型模块
3.2. “仿Java类”型模块
3.3. “立即执行函数(IIFE)”型模块
3.4. “全局变量输入”型模块
注:上面仅列举了传统模块化方法中的几种常见代码组织形式,还有“放大型”、“宽松放大型”等其它方法,这里就不一一列举了,有兴趣可以看看下面这篇文章... http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
传统模块化方法中
基本做到了让模块更独立、减少模块间冲突
但还有个更重要的问题没解决掉
如何清晰地描述模块间依赖
搞这么一大堆方法
优点肯定是有的
优点:传统模块化相比于无模块化时代,显然是进步的:减少了命名冲突,增强了模块的独立性;
但是
缺点同样明显
缺点: 1. 污染全局作用域:虽然我们通过各种手段尽力避免,但实际未从根本上解决; 2. 依赖关系不明显:对于大型项目,模块数量巨大,开发人员必须手动解决模块间依赖,这在复杂项目中极易出错且维护成本高
4. 百家争鸣:CommonJS、AMD、CMD
JavaScript 在语言层面迟迟不推出模块化功能,这个背景下,各“民间组织”提出了CommonJS、AMD、CMD 模块化规范...
——《高手在民间》
4.1. CommonJS
Node.js的诞生,使JavaScript扩展到了服务器端, 为了让JavaScript在服务器端能跟Java、Phyton一样编写大型程序,于是有了CommonJS模块化规范;
(1). CommonJS是针对服务器端(非浏览器环境)的JavaScript开发,是Node.js的默认模块化规范; (2). CommonJS是一种只适用于JavaScript的静态模块化规范; 注:只适用于JavaScript,意味着它无法把CSS等前端资源纳入模块化管理范围,但显然CSS也是组成前端模块的重要部分; 注:静态模块化规范,意味着它无法实现按需加载; (3). CommonJS所有模块均是同步阻塞式加载,无法实现异步加载; 注:服务器端加载模块是从硬盘直接读取,时间消耗和忽略不计;但浏览器端需要经网络下载,时间消耗取决于网速,同步加载策略容易出现“假死”,因此“同步阻塞式”加载策略不适用于浏览器环境;
示例:
CommonJS是针对服务器端JavaScript的规范
但不适用于浏览器端
于是衍生出针对浏览器端的
AMD和CMD规范
4.2. AMD
AMD(Asynchronous Module Definition),异步模块定义;
实现:RequireJS; 特性:依赖前置,提前执行;
示例:
4.3. CMD
CMD(Common Module Definition),通用模块定义;CMD与AMD很类似,只是在模块的运行、解析时机上有所不同;
实现:SeaJS; 特性:依赖就近,延迟执行;
示例:
5. 一统天下:ES6 Module
ES6在语言规格的层面上实现了模块功能,而且实现的相当简单,完全可以取代现有的CommonJS、AMD和CMD规范,成为浏览器和服务器通用的模块解决方案;
特点:
语言级、静态模块化规范; 实现按需加载的dynamic import语法提案现处于stage3阶段; 完全可替代CommonJS、AMD、CMD;
示例1(static import):
示例2(dynamic import):
总结一下
传统模块化手段:通过JS的闭包、对象、自执行函数等语言特性,避免模块间的命名冲突,提高模块的内聚性,但无统一编程标准,也无法把模块间的依赖关系描述清晰; CommonJS:Node.js让JavaScript延伸到“服务端”领域,促使针对“服务端”的JavaScript静态模块化规范CommonJS诞生,但此规范的“同步阻塞式”模块加载策略不适用于浏览器端环境; AMD,CMD:CommonJS规范的衍生品,支持模块“异步并行加载”,适用浏览器环境;AMD推崇“依赖前置”、CMD则是“依赖后置”;AMD规范的产物为RequireJS,CMD是SeaJS; ES6 Module:官方模块化标准,是语言的一部分,无需额外引入第三方库;ES6 Module同CommonJS一样,也是静态模块化规范,无法实现“按需加载”;但目前有一份处于stage3阶段的 dynamic import(tc39/proposal-dynamic-import)提案可用于动态模块加载;ES6完全可以取代CommonJS、AMD、CMD,成为浏览器和服务器端通用的模块化解决方案;