如今的网页越来越像桌面程序,网页上加载的javascript也越来越复杂,coder们不得不开始用软件工程的思维去管理自己的代码。Javascript模块化编程,已经成为一个非常迫切的需求。理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。但是,Javascript不是一种模块化编程语言,它不支持"类"(class),更遑论"模块"(module)了。(正在制定中的ECMAScript标准第六版将正式支持"类"和"模块",但还需要很长时间才能投入实用。)
说了半天概念应该印象还不深刻,我们就来看一个例子用来演示sea.js的基本用法。首先define传入的参数是对象和字符串的情况,我先举一个参数的对象的例子,传字符串大同小异。来看代码:
1,我先来定义一个模块m1.js:
define({a:"这里是属性a的值"});
define传入的是一个对象字面量。现在这个东东就可以叫做一个模块了~我想在页面一加载的时候就把a的值alert出来,怎么做呢?继续往下看。
2,在页面上引入这个模块:
1 seajs.use('./m1.js',function(ex){
2 alert(ex.a);
3 }); //弹出“这里是属性a的值”
翻译得直白一点,大意就是:
seajs : Hi~m1.js,我现在要用(use)你了,然后把你的公开接口(exports)存到我回调函数的参数(ex)里,你把想给我调用的东东放到这个参数里吧~么么哒
m1.js : 好的,我定义的对象字面量放到接口里给你了,拿去尽管刷~
然后……a的值就弹出来了。很愉快的一次交易。PS:页面所调用的模块就为整个web应用的js入口。本例中js的入口就是m1.js。接下来再来看看如果define的参数是个函数的情况。
1,先定义一个模块m2.js:
复制代码
1 define(function(require,exports,module){
2 var var1 = "这是要alert出来的值";//私有变量,没有通过接口返出去的其他模块不能访问
3 function alerts(){
4 alert(var1);
5 }
6 exports.alerts = alerts;//将需要公开的方法存入exports接口中
7 });
复制代码
2,在页面上引入这个模块并执行模块m2.js公开的方法:
1 seajs.use('./m2.js',function(ex){
2 ex.alerts();//ex中存的有m2.js中的公开对象
3 }); //弹出“这是要alert出来的值”
到这里可以简单地说一下factory方法的三个形参的意义了(个人理解):
require : 提供了引入机制,提供了一种方式来建立依赖,和C中的include和java中的import类似;
exports : 提供了导出机制,提供了私有和共有分离,未使用exports语句导出的变量或者函数,其他模块即使引用此模块也不能使用;
module : 提供了模块信息描述。
是不是思路贱贱清晰了呢?刚才我们的例子中只是从页面调用模块的用法,模块之间互相调用还没有体现,SO,接下来就以m1.js和m2.js两个模块作为例子来尝试一下 模块之间互相调用。
1,首先m1.js模块不变:
1 define({a:"这里是属性a的值"});
2,m2.js模块要依赖(require)m1.js:
复制代码
1 define(function(require,exports,module){
2 var var1 = "这是要alert出来的值";//私有变量,没有通过接口返出去的其他模块不能访问
3 var var2 = require('./m1.js').a;//这里就是m2.js模块调用m1.js的方式:var2的值等于当前模块所依赖的m1.js对外接口中属性a的值
4 function alerts(){
5 alert(var2);
6 }
7 exports.alerts = alerts;//将需要公开的方法存入exports接口中
8 });
复制代码
3,页面上引入m2.js模块(同上一个例子),结果就会把a的属性值给alert出来~
五、实例:模块化的拖拽个窗口缩放
当然,上面几个例子是简单到不能再简单的例子,估计亲们也已经看出来一些道道,但个人感觉还是没能体现出模块化开发的优势。那下面就来看一个实例:模块化的拖拽个窗口缩放。先看一下效果图:
PS:效果图中的红色区域要先定缩放的范围,即宽高0px-宽高500px。要写这样一个需求的例子,按照之前的编程习惯你会怎么写?反正在之前,我是会把所有的功能写到一个js文件里,效果出来就行,随你们怎么胡搅蛮缠。而自从认识了模块化开发,内心不止一次告诉自己,拿到需求bigger一定要高,一定要高(虽然require.js和sea.js这两个东东在圈内多多少少还是有些争议)……
废话少说,首先来分析一下需要划分多少个模块吧:
1,一开始就要有个入口模块的吧?恩,必须的!入口模块Get√~
2,既然是拖拽,要有个拖拽模块吧?恩,必须的!拖拽模块Get√~
3,既然要缩放,要有个缩放模块吧?恩,必须的!缩放模块Get√~
4,既然限定缩放范围<=500px,那还要有个限定缩放范围的模块吧?恩,这个可以有,但为了以后调整范围数值方便,还是单列个模块吧。限定缩放范围模块Get√~
到这里我们就把本需求划分成了四个模块:
· 入口模块:main.js
· 拖拽模块:drag.js
· 缩放模块:scale.js
· 限定缩放范围模块:range.js
首先,是页面引入入口模块(我尽量把注释都写在代码中,以便对照代码,这样也就不用写大片大片的文字了~):
1 <script>
2 seajs.use('./js/main.js');//没有callback函数表明引入后直接执行入口模块
3 </script>
接下来看看入口模块(main.js)里都应该有些神马东东吧:
复制代码
1 //入口模块
2 define(function(require,exports,module){
3 var $id = function(_id){return document.getElementById(_id);}
4 var oInput = $id("button1");
5 var div1 = $id("div1");
6 var div2 = $id("div2");
7 var div3 = $id("div3");//以上是获取页面元素的几只变量
8 require('./drag.js').drag(div3);//引入拖拽模块,执行拖拽模块接口中的drag方法并传参
9 exports.oInput = oInput;
10 oInput.onclick = function(){
11 div1.style.display = "block";
12 require('./scale.js').scale(div1,div2);//引入缩放模块,执行缩放模块接口中的scale方法并传参
13 }
14 });
复制代码
恩,还真是全面呢,把拖拽模块和缩放模块都引进来了。看看拖拽模块(drag.js)吧~
复制代码
1 //拖拽模块
2 define(function(require,exports,module){
3 //这个方法就是实现拖拽的方法,不用详述了吧?
4 function drag(obj){
5 var disX = 0;
6 var disY = 0;
7 obj.onmousedown = function(e){
8 var e = e || window.event;
9 disX = e.clientX - obj.offsetLeft;
10 disY = e.clientY - obj.offsetTop;
11 document.onmousemove = function(e){
12 var e = e || window.event;
13 var l = require('./range.js').range(e.clientX - disX, document.documentElement.clientWidth - obj.offsetWidth,0);
14 var t = require('./range.js').range(e.clientY - disY, document.documentElement.clientHeight - obj.offsetHeight,0);
15 obj.style.left = l + "px";
16 obj.style.top = t + "px";
17 }
18 document.onmouseup = function(){
19 document.onmousemove = null;
20 document.onmouseup = null;
21 }
22 }
23 }
24 exports.drag = drag;//返回拖拽模块中想要被公开的对象,也就是在本模块中定义的drag方法。注意有参数~
25 });
复制代码
接下来是缩放模块(scale.js)。缩放模块还需要调用 限定缩放范围模块 (range.js) 的哦~这点不要搞忘了。
复制代码
1 //缩放模块
2 define(function(require,exports,module){
3 //这个方法就是obj2控制obj1改变大小的方法,也不再详述啦~
4 function scale(obj1,obj2){
5 var disX = 0;
6 var disY = 0;
7 var disW = 0;
8 var disH = 0;
9 obj2.onmousedown = function(e){
10 var e = e || window.event;
11 disX = e.clientX;
12 disY = e.clientY;
13 disW = obj1.offsetWidth;
14 disH = obj1.offsetHeight;
15 document.onmousemove = function(e){
16 var e = e || window.event;
17 var w = require('./range.js').range(e.clientX - disX + disW,500,100);//看这里看这里,引入了限定范围的range.js模块~
18 var h = require('./range.js').range(e.clientY - disY + disH,500,100);
19 obj1.style.width = w + "px";
20 obj1.style.height = h + "px";
21 }
22 document.onmouseup = function(){
23 document.onmousemove = null;
24 document.onmouseup = null;
25 }
26 }
27 }
28 exports.scale = scale;//将需要公开的对象存入模块接口中,以便其他模块调用~
29 });
复制代码
最后就是限定范围的模块(range.js)了。
复制代码
1 //限定拖拽的范围模块
2 define(function(require,exports,module){
3 function range(inum,imax,imin){
4 if(inum > imax){
5 return imax;
6 }else if(inum < imin){
7 return imin;
8 }else{
9 return inum;
10 }
11 }
12 exports.range = range;
13 });