JS模块化编程
来源:互联网 发布:软件测试就业前景 编辑:程序博客网 时间:2024/04/29 03:06
1. 背景
JS的强大已经不用解释了,从Web RIA到Node服务器,到处都是JS的身影。然而由于出身的缘故,JS本身在大规模应用上存在着很多问题,比如模块化编程。本文以浏览器端模块化编程为内容,重点介绍AMD规范和CMD规范。在服务器端,NodeJS遵守的Common规范我们这里暂不讨论。
对于计算机语言,模块化编程是必不可少的,对架构设计、代码复用起到至关重要的作用,工程中引入别人写好的库和模块能大大缩减开发周期。C/C++中,我们可以用include;Java中我们可以用import。可是在JS中,这些是不存在的,取而代之的是script标签。
在浏览器端,我们可以通过script标签引入需要的库,然后加载自己的脚本,最后运行脚本。这样做似乎与include和import没什么区别,然而有几个重要的因素必须要考虑:JS是解释型语言,边加载边运行,后续脚本运行时,这些脚本所依赖的一切必须已经加载完毕;JS脚本加载时会阻塞浏览器,如果加载的JS很多很大,浏览器会卡住,带来很差的用户体验;通过调整script标签顺序可以修改JS模块之间的依赖,然而当模块很多时,这种做法就不可用了。
鉴于以上,JS模块化编程规范应运而生,并出现了大量实现。这些实现,简单讲就是一个超底层的JS库,这个库的作用有两个:一是完成JS脚本的异步加载与执行;二是确保JS脚本按照用户设计的依赖关系加载,即一段脚本执行时,它的所有依赖已经加载完毕了。不同的规范,只是写法用法不同而已,最终目的都是一样的。另外,模块化编程与异步加载不分家,会让JS按需加载,大大提高页面响应速度;模块加载提供命名空间的效果,有效防止作用域污染,并有一定的容错能力。
AMD规范和CMD规范是目前最为流行的两种模块化编程模式,符合AMD规范的库有RequireJS,符合CMD规范的库有SeaJS。
2. AMD(Asynchronous ModuleDefinition)规范
AMD翻译过来叫“异步的模块定义”,接口非常简单,只有一个define,完整格式如下:
- define(‘module-name’, [‘lib1’, ‘lib2’], function (lib1, lib2) {
- // todo
- return {};
- });
第一个参数是模块的名称,可以省略;第二个是模块的依赖,也就是其他模块,如果省略,则此模块没有任何依赖;第三个参数是依赖加载完毕后的回调函数,回调函数的形参是依赖模块的输出,顺序与第二个数组参数一致,回调函数的返回值就是此模块的输出,可以作为其他模块的依赖形参。
2.1 纯数据模块
这种模块没有任何依赖,不做任何操作,只是为了提供数据源,可以这样定义:
- define({
- color: "black",
- size: "unisize"
- });
2.2无依赖模块
如果模块没有任何依赖,但进行了一些预处理,可以这样定义:
- define(function() {
- // TODO
- return {
- color: "black",
- size: "unisize"
- }
- });
2.3将模块定义为函数
这种模块是最常见的,意为模块的输出是一个函数,模块的依赖是这个输出函数使用的外部变量,因此这种模块的输出是一个闭包。使用时也要格外小心,尤其是输出函数当做构造函数使用时。
- define(["my/cart", "my/inventory"],
- function(cart, inventory){
- //return a function todefine "foo/title".
- //It gets or sets thewindow title.
- return function(title){
- return title ? (window.title= title) :
- inventory.storeName + ' ' + cart.name;
- }
- }
- );
2.4引入符合Common规范的模块
- define(function(require){
- var mod = require("./relative/name");
- return mod;
- });
2.5 JSONP模块
JSONP可以跨域,应用非常广泛,使用JSONP模块时,callback必须设成’define’。
- require(["http://example.com/api/data.json?callback=define"],
- function (data) {
- //The data object will be the APIresponse for the
- //JSONP data call.
- console.log(data);
- }
- );
这种方法一般用于初始化,但错误处理比较复杂。
3. CMD规范
CMD(Common ModuleDefinition)规范,跟AMD相比,CMD更像CommonJS。至于CMD和AMD哪个好哪个坏,完全没有定论的,主要看个人喜好。CMD的主接口也叫define,其参数可以是string、object、function。define的用法也可以像AMD一样,带id、依赖、回调,但这样就不属于CMD规范了。基本格式如下:
- define(function (require, exports, module) {
- // todo
- });
3.1 require参数
第一个形参require是一个方法,用来加载外部模块,用法跟2.4类似:
- define(function (require, exports) {
- // 获取模块 a 的接口
- var a = require('./a');
- // 调用模块 a 的方法
- a.doSomething();
- });
但是这个require是同步模式加载,即如果模块没加载完毕,require后面的语句是不会执行的。AMD规范中都是异步加载,在回调function中使用require,其实相当于告诉构建器要在依赖队列中加入相关模块。
require同样有异步加载功能,异步加载后,执行响应的回调:
- define(function(require, exports, module) {
- // 异步加载一个模块,在加载完成时,执行回调
- require.async('./b',function(b) {
- b.doSomething();
- });
- // 异步加载多个模块,在加载完成时,执行回调
- require.async(['./c','./d'], function(c, d) {
- c.doSomething();
- d.doSomething();
- });
- });
3.2 exports参数
exports是一个对象,是模块的对外接口,即AMD规范中的return部分,当然,也可以像AMD规范一样,利用return返回接口。两种方式都可以,非常灵活。
- define(function(require, exports) {
- // 对外提供 foo 属性
- exports.foo = 'bar';
- // 对外提供 doSomething 方法
- exports.doSomething =function() {};
- });
- define(function(require) {
- // 通过 return 直接提供接口
- return {
- foo: 'bar',
- doSomething: function() {}
- };
- });
值得注意的是,exports形参是不能重新定义的,因为重新定义后,原引用就断掉了,下面的做法起不到输出模块的作用:
- define(function(require, exports) {
- // 错误用法!!!
- exports = {
- foo: 'bar',
- doSomething: function() {}
- };
- });
3.3 module参数
module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。
module.id为模块标识,module.uri为模块绝对路径,module.dependencies为模块的依赖数组,module.exports为模块的输出引用。也就是说,3.2节最后的例子中,如果这样写,就是可以工作的:
- define(function(require, exports, module) {
- // 正确写法
- module.exports = {
- foo: 'bar',
- doSomething: function() {}
- };
- });
同时,对module.exports的赋值必须是同步的,放在settimeout里面是不行的。
4.总结
可以看出,模块化编程为我们提供了良好的环境,在这个环境中,可以进行更明确的分工合作,让JS开发更专业化。同时,AMD规范和CMD规范都提供了异步加载,让Web前端应用更加高效,用户体验更好。
至于这两种规范哪个好一些,这个完全看个人编码习惯了,适合你的,就是好的。不过总体来看,CMD有相当一部分兼容了AMD,其最大的区别还是对依赖的引用上。
AMD主要通过回调函数的形参对依赖进行引用,有多少个依赖,就有多少个形参,当然如果依赖很多也可以不写那么多形参,通过arguments引用,或者在回调函数内部require也是一样的。这里需要说明的是,AMD规范中,尤其是RequireJS中,如果通过var a = require(‘module-name’);这种形式引入依赖,那么module-name必须要在依赖数组中存在,否则会报错。
CMD规范的依赖引用主要是在回调函数中直接require了,并且这种require是同步的,也提供了异步接口,这样做,势必要影响运行期性能。但是想AMD那种预加载依赖的方式,势必要影响加载时间。这两种影响哪个能容忍,哪个不能容忍,应该具体情况具体分析,视应用而定。
5.参考
CMD规范定义:
https://github.com/seajs/seajs/issues/242
JavaScript的AMD:
http://www.cnblogs.com/happyPawpaw/archive/2012/05/31/2528864.html
RequireJS中文网:
http://www.requirejs.cn/
Sea.js文档:
http://seajs.org/docs/#docs
- Js 模块化编程
- JS模块化编程
- JS模块化编程
- JS模块化编程
- js模块化编程
- js模块化编程
- JS模块化编程总结
- JS模块化编程
- js模块化编程
- JS模块化编程
- js模块化编程
- js模块化编程
- js模块化编程
- js模块化编程总结
- js的模块化编程
- js模块化编程 : require.js
- JS模块化编程 Require.JS
- javascript 模块化编程 require.js
- 双向链表的插入及删除图解
- powerdesigner中使用mysql逆向工程生成pdm,附带解决注释乱码
- 欢迎使用CSDN-markdown编辑器
- MYSQL关联两表或多表执行修改删除操作
- c 程序设计语言 第二版 练习题 5-4
- JS模块化编程
- 蓝桥杯 生日蜡烛
- 图像卷积
- SAP BusinessOne:福奈特选择奥维奥SAP BusinessOne 解决方案
- CentOS VI常用命令
- 2016 版 Laravel 系列入门教程(五)【最适合中国人的 Laravel 教程】
- git使用IDEA工具更新代码,导致未提交代码被覆盖解决办法
- 分布式缓存-Memcached
- Mysql查询今天/昨天/15天前/上个月/去年/上周每日等函数