JavaScript模块化开发意义与方法

1.为什么要进行模块化开发
模块化开发:为了避免命名空间冲突,为了更优雅地使用文件依赖。
看下面代码:

var say = function (something) {
	console.log(something);
};
say('something');

A在页面抽取公共say方法,用于控制台输出测试信息,如果其他人要使用这个公共方法时,只要调用say方法即可.
但是问题来了,如果项目比较大,B在页面中其他位置同样也定义了同名函数say,如下代码:

var say = function(something){
	alert(something);
}

这时,B定义的say方法就会覆盖A定义的say方法。导致之前的代码得到不一样的效果。
然后C决定改写这段代码,分配命名空间,代码如下:

var obj1 = {
	say:function (something) {
		console.log(something);
	}
};
var obj2 = {
	say: function(something){
		alert(something);
	}
};

如果需要使用A定义的say方法只要调用obj1.say方法即可;使用B定义的say方法只需调用obj2.say方法即可实现。
虽然此方法避免了命名空间冲突的问题,但是为了使用一个方法需要记住这么长的命名空间,也不是长久之道,于是D又进行了一次改写,代码如下:

YUI().use('a',function(Y){
	//加载a模块
	//下面可以通过Y来调用a模块
	Y.say();
});

但是这样又引入了新的问题,如下代码:

YUI().use('a','b',function(Y){
	Y.say();
});

我们不知道say方法是属于a模块还是b模块,而且如果两者都提供了say方法,如何解决冲突?如何优雅地使用文件依赖?
命名冲突和文件依赖,是前端开发过程中的两个经典问题。而模块开发能解决此问题。
2.如何进行模块化开发?
2.1、使用Sea.js(CMD规范)
使用sea.js需要遵循CMD规范进行书写。一个文件就是一个模块。例子如下:

//a.js
define(function(require,exports){
	exports.say = function (something) {
		console.log(something);
	};
});
//r.js
define(function(require,exports){
	var a = require('./a.js');
	exports.test = function (){
		a.say('test');
	};
});
//在页面中使用r.js
seajs.use('r',function(r){
	r.test('r');
});

如上代码,模块a.js通过exports向外暴露接口,模块r.js通过require引用a.。
这里的require可以认为是Sea.js给JavaScript语音增加的一个语法关键字,通过require可以获取其他模块提供的接口。
Sea.js带来的好处:
(a)通过exports暴露接口。这意味着不需要命名空间了,更不需要全局变量。这是一种彻底的命名冲突解决方案。
(b)通过 require 引入依赖。这可以让依赖内置,开发者只需关心当前模块的依赖,其他事情 Sea.js 都会自动处理好。对模块开发者来说,这是一种很好的 关注度分离,能让程序员更多地享受编码的乐趣。
2.2、使用require.js

define('header',['public'],function(){
	//do something
	var init = function(){

	};
	return init;
});

2.3、使用CommonJS
目的是:定义很多普通应用程序(主要指非浏览器的应用)使用的API。
NodeJS、npm均遵循CommonJS规范。
使用方法:

var math = require('math');
math.add(2, 3);

但是这带来一个很大的问题:
math.add(2, 3);方法的执行必须等到math模块加载完成之后才能进行。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的。
这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。
因此,浏览器端的模块,不能采用‘同步加载’(synchronous),只能采用‘异步加载’(asynchronous)。这就是AMD规范诞生的背景。
2.4、使用modJS
modJS是百度fex-team提供的一个轻量级的模块加载器,类似requirejs。
但modJS并不完全兼容规范amd/cmd,事实上,只支持非常简单的全局方法define(id,factory)。
另外factory提供了3个参数require/exports/module,用于引用和导出模块。
modJS源码只有200行左右,相比之下requirejs的源码达到了2000+行。
除了体量非常小之外,modJS配合fis3的fis3-hook-commonjs插件,可以在纯前端项目中实现类似nodejs一样的开发体验。
ModJS严格上来讲并不是一个独立的模块化框架,它是被设计用做前端工程化模块化方案的JavaScript支持,需要和自动化工具、后端框架配合来使用,目的在于希望给工程师提供一个类似nodeJS一样的开发体验,同时具备很好的线上性能。

//定义模块,define(id,factory);
define('c.js',function(require.exports,modules){
	var a = require('a');//所需模块已经预先加载
	exports.init = function(){
		a.say();
	};
});
//模块调用,require(id)
require('id');

参考文章:
前端模块化开发的价值
简单介绍下modJS
ModJS与RequireJS/SeaJS的那些事

此条目发表在JavaScript分类目录。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用*标注