Seajs模块化开发Javascript - 用代码说话(一)

  前言

    模块化开发JS已经逐渐开始普及,在RequireJS和SeaJS中犹豫了2天,最后选择从SeaJS入手.网上有不少教程和使用方法,不过理论东西太多了,我是标准的实用主义和拿来主义者,所以直接用代码说话.

  正文

    先上一个最简单的Demo,比官网上的简单无数倍:

    从官网下载最新版本的Sea.js文件,我下载的是2014年3月19的2.2.1版:https://github.com/seajs/seajs/archive/2.2.1.zip.

    我们就需要dist中的sea.js即可,接下来在dist文件夹同目录下创建demo.html,util.js,componet_one.js这三个文件.

    util.js文件中定义一个功能,单独作为一个模块:

//util.js
define(function (require,exports,module) {  
    function print (str) {    
    // body...    
    alert(str);  
    }  
    exports.print = print;
});

    然后定义componet_one.js,它的实现依赖于util.js:

//componet_one.js
define(function (require,exports,module) {  
    var util = require("./util.js");  
    var ComponetOne = {    
        doSth : function(){      
        util.print("I'm a String.")    
        }  
    }  
    return ComponetOne;
})

    最后在index.html页面上,直接开始调用:

<!doctype html>
<html>
    <head>
      <meta charset="UTF-8">
    <title>SeaJS</title>
    </head>
    <body>
      <script type="text/javascript" src="dist/sea.js"></script>
        <script type="text/javascript">
            seajs.use("./componet_one.js",function(componet_one){
                  componet_one.doSth();
            });  
        </script>
    </body>
</html>

    页面上显示的结果是弹出一个对话框上面写着:

"I'm a String."

    示例完毕,感觉如何,我认为即使对模块化或者Sea.js和RequireJS一点概念都没有,只要有一定的Javascript基础都能理解或者说猜出上面代码的功能和内容.作为理解模块化的入门以及Sea.js应该算是最简单的代码了.

    代码虽少,不过还是可以很直观的看出一些东西的,所以通过这几段简单代码来引入一些Sea.js中的API和使用方法.因为Sea.js遵循的是CommonJS规范,所以它的API使用起来虽然看着和RequireJS很像,但还是有所不同,并且它们内部实现也不一样.

    

    (1) CMD模块定义规范

    在上面的util.js中通过define()定义了一个模块,可以看到全局函数define就接受了一个函数参数.其实define还可接受对象或者字符串为参数,这时表示模块的接口就是这对象、字符串.

define({"foo" : "bar});
define("It's a template");

    这都是可以的,大多数的情况下还是以函数为参数,表示模块的构造方法.执行这个构造方法可以得到模块向外提供的接口,比如上面utils.js中就是如此.这种情况下方法执行时,默认传入三个参数:require、exports以及module.

    如果你不知道是否可以在当前开发项目中使用define,可以通过define.cmd进行判断当前页面是否有CMD模块加载器:

if (typeof define === "function" && define.cmd){    
    //此时表示存在Sea.js等CMD模块加载器
}

  

    (2) 通过exports对象向外提供模块接口

    在util.js中define定义的方法体内,有一行代码是exports.print = print;什么都不看的解释是将方法体内定义的print函数指向exports的print属性.

    exports是define()定义模块时,Function传入的三个默认参数之一,通过它可以对外提供属性或者方法.

    除了给exports对象增加成员,也可以通过return直接向外提供接口,将util.js改为return的方式就如下:

define(function (require,exports,module) {  
    return {   
        print : function(str) {
              // body...      
              alert(str);    
        }
     }
});

    还可以通过module.exports进行赋值,module是define()的参数为函数参数时的函数参数的一个函数.这么说有点费劲,那么我对之前说的define进行一点补充.

    标准说法的define,应该是define(factory)作为define的参数,factory可以使一个函数,也可以是一个对象或者字符串.所以说require、exports和module都是factory的默认参数.

    util.js通过module.exports的写法如下,效果是一样的:

define(function (require,exports,module) {  
    module.exports =  {   
        print : function(str) {
              // body...
              alert(str);
        }
     }
});

    exports仅仅是module.exports的一个引用,在factory内部给exports重新赋值时,不会改变module.exports的值,因此给exports赋值是没有效果的,不能用来改变模块接口.这个地方exports和module.exports的关系和Nodejs中是完全一样的.


    (3) 存储当前模块属性和方法的module

     既然上面以及提到了module,这里就接着往下说,module中最有意义也是需要掌握的就是module.exports,这个在上面说exports的时候提到了.传给factory构造方法的exports参数是module.exports对象的一个引用.仅仅通过exports参数提供接口有时无法满足需求.

    例如当模块的接口是某个类的实例时,就要通过module.exports来实现:

define(function (require,exports,module) {    
    //exports 是 module.exports 的一个引用  
    console.log(module.exports === exports);  //true
    module.exports = new SomeClass();  
    console.log(module.exports === exports);  //false
});

    

    (4) 通过require获取模块

    通过define、exports以及module的介绍相信util.js中的代码是什么意思并不难理解,然后看一下componet_one.js文件,它也是一个通过define定义的模块,在factory中第一行:var util = require("./util.js");

    如果之前用过Nodejs,那么对于require就很熟悉了.它通过一个模块标识作为唯一参数,用来获取模块提供的接口.这里就是获取到了util模块,可以使用util.js文件中定义的内容,这点没什么疑问.

    require还有一个实现就是require.async方法用来在模块内部异步加载模块,并在加载完成后执行回调函数.

define(function(require,exports,module){    
    //异步加载一个模块,加载完成后执行callback  
    require.async('./b',function(b){
        b.dosth();
    });
    //异步加载多个模块,然后执行callback
    require.async(['./c','./d'],function(c,d){
        c.dosth();
        d.dosth();
    });
});

    结尾

        类似RequireJS和SeaJS这类模块加载器,它们的实质是为了弥补Javascript在设计之处的不足,可以想象就连Javascript的发明者也没有预料到今天Javascript的火热和普及程度,所以在设计上没有做好文件组织管理这方面的功能.

    因为早期它看起来确实就是一种简单的脚本语言,一个页面也就一个JS文件直接引入就好了.但是随着Javascript火爆的发展以及大量的应用,这明显成为其短板,所以广大优秀的Coder们想出了各种办法进行解决.

    模块加载主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载,将前端工程师从繁重的JavaScript文件及对象依赖处理中解放出来,可以专注于代码本身的逻辑.

        后续会逐渐写一些更实用的东西,欢迎留言给我,一起学习~

你可能感兴趣的:(JavaScript,模块化,seajs)