JavaScript中的模块化之AMD和CMD
前言:
为什么我们需要模块化开发,模块化开发的好处有哪些? 首先我们先说一下非模块化的开发方式带来的弊端。 非模块化开发中会导致一些问题的出现,变量和函数命名可能相同,会造成变量污染和冲突,并且出错时候很难排查。耦合程度高,不符合软件开发中的高内聚和低耦合的原则,所以我们就可以总结一下模块化开发的好处了:
① 解决项目中的变量污染问题。
② 开发效率高,有利于多人协同开发。
③ 职责单一,方便代码重用和维护 。
③ 解决文件依赖问题,无需关注引包顺序 。
模块化开发的演变过程
- 普通的函数封装
- 封装成对象
- 私有公有成员分离 (使用自执行函数,避免变量污染)
- 模块的维护和扩展(用多个自执行函数把模块分离开来,使用开闭原则—去增添功能,而尽量不要修改原来的代码,)
- 可以添加模块的第三方依赖(比如添加jQuery的$,$作为一个编程的接口,降低程序之间的耦合度)
模块化的规范
服务端规范 — CommonJs
以为作为服务器端的开发,不会经过网络传输,所以包的加载几乎都是瞬间完成的,只有加载完成了,才能执行操作,所以commonjs是同步的,nodejs就是实现了这种规范。
有关规范的具体详情,找了两篇文章:
http://www.open-open.com/doc/view/f7df10bb81c347f79b436faa85dcfd81
http://blog.jobbole.com/49290/
浏览器端规范 — AMD 和 CMD
浏览器端的规范,因为所有的文件请求都要经过网络传输,很多文件的加载会有不确定因素存在为了解决这个问题,浏览器端出来了这两个规范。
AMD规范:
异步模块定义规范(Asynchronous Module Definition)制定了定义模块的规则,这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题)。具体规范链接在这里:中文版 英文版 ,requireJs就是AMD规范的实现。
- requirejs的基本使用: 
 ① 引入requirejs包,并且设置入口文件- <script data-main='js/main' src='http://apps.bdimg.com/libs/require.js/2.1.9/require.min.js'></script>
 - 1
- 2
 - 1
- 2
 - ② 定义主模块 在main.js文件中 - define(['module1','module2',function(m1,m2) {
 // 说明:上面两个module是依赖模块,通过m1,m2的方式来使用
 // 在这里我们写自己的业务逻辑 todo
 // 如果我们需要暴露出去,需要 return ,不是用exports或者module.exports
 // return {};
 }]);- 1
- 2
- 3
- 4
- 5
- 6
 - 1
- 2
- 3
- 4
- 5
- 6
 - 后续的使用,直接看文档吧,以后有机会再来补充。 
 requirejs官网
 requirejs中文官网
CMD规范:
通用模块定义 (Common Module Definition) 是 SeaJS 在推广过程中对模块定义的规范化产出。类似的还有 CommonJS Modules/2.0 规范。
这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。
目前这些规范的实现都能达成浏览器端模块化开发的目的。有两篇文档相关文档: 文档1 文档2 , 下面简要说一下seajs, 详细资料还得戳旁边的资料文档。
- seajs 的基本使用 - 引入包文件 - <script src='http://apps.bdimg.com/libs/seajs/2.3.0/sea.js'></script>
 - 1
- 2
 - 1
- 2
 
- 定义一个模块,比如在这个文件 student.js 中 - define(function(require,exports,module){
 function Student(){
 this.name = '张三';
 }
 // 对外暴露该模块的接口:
 module.exports = new Student();
 })- 1
- 2
- 3
- 4
- 5
- 6
- 7
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
 
- 使用模块:通过 - seajs.use()来实现,第一个参数是使用模块的路径,第二个回调函数中的参数是所要使用模块暴露出来的一个接口。- seajs.use('./student.js',function(stu){
 console.log(stu.name); // 张三
 });
 - 1
- 2
- 3
- 4
 - 1
- 2
- 3
- 4
 
 
- seajs 暴露出接口的方式 - 通过 module.exports方式导出对象
- 通过 exports.属性或方法方式导出
- 两者的区别和联系:module.exports 和exports 指向的是同一个对象。exports是函数内的一个形式参数,而module.exports是一个实实在在的对象,一般在导出的时候用 module.exports 指向一个新的对象,而最终模块都是用module.exports来导出的。
 
- 通过 
- seajs 内部的异步加载简单小demo — 写个小demo来阐述一下这个异步加载机制,在demo中,有以下几个文件 index.html ,loadJs.js ,module1.js: - index.html文件中的关键代码 - <!--引入cdn上的seajs文件-->
 <script src='http://apps.bdimg.com/libs/seajs/2.3.0/sea.js'></script>
 <script src='loadJs.js'></script>
 <script>
 loadJs('module1.js',function(){
 console.log('回调内的函数最后执行');
 });
 </script>
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
 
- loadJs.js文件中的代码 - function loadJs(path,callback) {
 var head = document.getElementsByTagName('head')[0];
 var script = document.createElement('script');
 script.setAttribute('src',path);
 head.appendChild(script);
 if(!/*@cc_on!*/false) {
 // 非IE浏览器
 script.onload = function() {
 console.log('非IE浏览器');
 callback();
 }
 }else{
 script.onreadystatechange = function(){
 if(script.readyState === 'loaded' || script.readyState === 'complete') {
 console.log('IE');
 callback();
 }
 }
 }
 }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
 
- module1.js文件中的代码: - console.log('我是module1文件内的');
 - 1
- 2
 - 1
- 2
 
- 在Chrome下测试,最后控制台的输出结果: - 我是module1文件内的
 非IE浏览器
 回调内的函数最后执行- 1
- 2
- 3
 - 1
- 2
- 3
 
- 总结:在模块加载的过程中,先执行依赖模块内的逻辑,最后再去执行回调函数内的逻辑 - seajs 异步加载 require 中的async方法: - require.async() 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。callback 参数可选。 require 是同步往下执行,require.async 则是异步回调执行。require.async 一般用来加载可延迟异步加载的模块。 
- seajs 的第三方依赖库的引入:比如 jQuery,我们需要使用jquery,加入到我们的项目中,首先我们需要对jquery 进行改造,在高版本的jquery文件比如2.2中支持amd规范但不支持cmd规范,在文件中的最后找到如下代码: - if ( typeof define === "function" && define.amd ) {
 define( "jquery", [], function() {
 return jQuery;
 } );
 }- 1
- 2
- 3
- 4
- 5
 - 1
- 2
- 3
- 4
- 5
 - 我们把它改造为: - if ( typeof define === "function" && (define.amd || define.cmd) ) {
 define( "jquery", [], function() {
 return jQuery;
 } );
 }- 1
- 2
- 3
- 4
- 5
 - 1
- 2
- 3
- 4
- 5
 - 然后我们可以通过加载模块的方式,把jQuery加载进去了。 
 
 
- seajs的配置调试: - https://github.com/seajs/seajs/issues/262 
 API上讲的很详细。
- 简单的写一写: - seajs.config({
 // 别名配置
 alias: {
 'es5-safe': 'gallery/es5-safe/0.9.3/es5-safe',
 'json': 'gallery/json/1.0.2/json',
 'jquery': 'jquery/jquery/1.10.1/jquery'
 },
 // 路径配置
 paths: {
 'gallery': 'https://a.alipayobjects.com/gallery'
 }
 })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
 
- 通过对 sea.js 进行配置,让模块编写、开发调试更方便。 
 
- https://github.com/seajs/seajs/issues/262 
- seajs 中的module - 看 API 中的module Object
 
Seajs和RequireJS的区别
— 引自 玉伯
- 两者定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。SeaJS 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 服务器端。
- 两者遵循的标准有差异。RequireJS 遵循的是 AMD(异步模块定义)规范,SeaJS 遵循的是 CMD (通用模块定义)规范。规范的不同,导致了两者 API 的不同。SeaJS 更简洁优雅,更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
- 两者社区理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。SeaJS 不强推,而采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
- 两者对调试等的支持有差异。SeaJS 通过插件,可以实现 Fiddler 中自动映射的功能,还可以实现自动 combo 等功能,非常方便便捷。RequireJS 无这方面的支持。
- 两者的插件机制有差异。RequireJS 采取的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS 采取的插件机制则与 Node 的方式一致:开放自身,让插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。
- 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
- CMD 推崇依赖就近,AMD 推崇依赖前置。
JavaScript中的模块化之AMD和CMD的更多相关文章
- JavaScript 中的模块化
		JavaScript 中的模块化 最早的基于立即执行函数,闭包的模块化 const MountClickModule = function(){ let num = 0; const handle ... 
- 前端模块化和AMD、CMD规范
		前端模块化和AMD.CMD规范 先看下基础:https://github.com/seajs/seajs/issues/547 
- 【整理】 JavaScript模块化规范AMD 和 CMD 的区别有哪些?
		根据玉伯等人在知乎上的回答整理.整理中... AMD 规范在这里:https://github.com/amdjs/amdjs-api/wiki/AMD CMD 规范在这里:https://githu ... 
- 浅析JS中的模块规范AMD和CMD
		一.AMD AMD就只有一个接口:define(id?,dependencies?,factory); 它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中,像这样: d ... 
- 前端模块化,AMD与CMD的区别
		最近在研究cmd和amd,在网上看到一篇不错的文章,整理下看看. 在JavaScript发展初期就是为了实现简单的页面交互逻辑,寥寥数语即可:如今CPU.浏览器性能得到了极大的提升,很多页面逻辑迁移到 ... 
- js模块化规范AMD、CMD、CommonJS...
		1. AMD 1.1 什么是AMD? AMD 英文名 Asynchronous Module Definition ,中文名 异步模块定义 .这是一个浏览器模块化开发的规范. 由于浏览器环境执行环境的 ... 
- 简单聊一聊Javascript中的模块化
		在面试中只要说到模块化的问题,多多少少总会问到这些,umd.amd.cjs.esm,可能听过其中一个两个,或者都听说过.接下来我们先简单了解一下他们到底是什么,又有什么样的区别呢. 最开始的时候,Ja ... 
- 回顾:前端模块化和AMD、CMD规范(全)
		先列举下一些著名言论: "我想定义一个 each 方法遍历对象,但页头的 util.js 里已经定义了一个,我的只能叫 eachObject 了,好无奈." "Requi ... 
- amd、cmd、CommonJS以及ES6模块化
		AMD.CMD.CommonJs.ES6的对比 他们都是用于在模块化定义中使用的,AMD.CMD.CommonJs是ES5中提供的模块化编程的方案,import/export是ES6中定义新增的 什么 ... 
随机推荐
- C++中的垃圾回收和内存管理(续)
			boost memory的gc_allocator的使用 首先编译生成boost-memory的库,由于生成的是.so的动态库,所以需要在运行程序之前,将库文件的路径添加到LD_LIBRARY_PAT ... 
- [BZOJ1602&BZOJ1787&BZOJ2144]树上LCA的算法巩固练习
			简述求LCA的倍增算法 对于树上的所有节点,我们可以很轻松地通过dfs求出其直接的父亲节点以及其深度 通过类似RMQ的原理我们可以处理出每个节点的第2^i个父亲 //这个过程既可以在dfs之后双重循环 ... 
- stdafx.h、stdafx.cpp的作用
			这两个文件用于建立一个预编译的头文件".PCH"和一个预定义的类型文件"STDAFX.OBJ".由于MFC体系结构非常大,各个源文件中都包含许多头文件,如果每次 ... 
- AtCoder Regular Contest 092 C D E F
			C - 2D Plane 2N Points 题意 二维平面上有\(N\)个红点,\(N\)个蓝点,一个红点和一个蓝点能配成一对当且仅当\(x_r<x_b\)且\(y_r<y_b\). 问 ... 
- linux驱动基础系列--linux spi驱动框架分析
			前言 主要是想对Linux 下spi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型等也不进行详细说明原理.如果有任何错误地方,请指出,谢谢! spi ... 
- 使用 .NET Core 的日志记录
			如何使用 Microsoft.Extensions.Logging public static void Main(string[] args = null) { ILoggerFactory ... 
- HDU 6373.Pinball -简单的计算几何+物理受力分析 (2018 Multi-University Training Contest 6 1012)
			6373.Pinball 物理受力分析题目. 画的有点丑,通过受力分析,先求出θ角,为arctan(b/a),就是atan(b/a),然后将重力加速度分解为垂直斜面的和平行斜面的,垂直斜面的记为a1, ... 
- HDU 6319.Problem A. Ascending Rating-经典滑窗问题求最大值以及COUNT-单调队列 (2018 Multi-University Training Contest 3 1001)
			2018 Multi-University Training Contest 3 6319.Problem A. Ascending Rating 题意就是给你长度为k的数列,如果数列长度k<n ... 
- 百度之星资格赛 2016 Problem 1002
			本文链接:http://www.cnblogs.com/Ash-ly/p/5494623.html 题意: 度熊面前有一个全是由1构成的字符串,被称为全1序列.你可以合并任意相邻的两个1,从而形成一个 ... 
- Codeforces #432 Div2 D
			#432 Div2 D 题意 给出一些数字,如果这些数字的的 \(gcd\) 不为1则称这些数字 \(good\). 可以有两种操作: 花费 x 删掉一个数 花费 y 将一个数加 1 问使这些数 \( ... 
