站长杂谈 > web前端
web前端web前端

NodeJs模块基础

在开发一个复杂的应用程序的时候,我们需要把各个功能拆分、封装到不同的文件,在需要的时候引用该文件。没人会写一个几万行代码的文件,这样在可读性、复用性和维护性上都很差,几乎所有的编程语言都有自己的模块组织方式,比如Java中的包、C#中的程序集等,node.js使用模块和包来组织,其机制实现参照了CommonJS标准。
 
NodeJs采用模块方式,管理和组织代码,NodeJs的所有的功能都存在每个模块中。
  
1.1    什么是模块
最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。
 
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script>
 
这段代码依次加载多个js文件。
 
这样的写法有很大的缺点。
 
首先、加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;
其次、由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
第三、如果在1.js里面定义了一个变量aa,在b.js里面又定义了一个变量aa,变量之间就会冲突。
 
模块化开发解决的问题:
 
(1)实现js文件的异步加载,避免网页失去响应。
(2)管理模块之间的依赖性,便于代码的编写和维护。
(3)避免污染全局变量。
 
模块:一个具有特定功能的文件就是一个模块模块之间可能存在一定的依赖关系,使用模块可以很好的把这些依赖关系整合起来。比如jquery的幻灯片插件依赖与jquery核心模块。
 
模块优点:有了模块,我们就可以非常方式的使用这些模块,因为模块总是完成了特定功能,如果需要修改模块中的功能,那么需要修改这个自己的模块文件即可,模块独立与每一个文件中,不影响模块的代码。
 

 
有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。
但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!考虑到Javascript模块现在还没有官方规范.
 
目前,通行的Javascript模块规范共有三种:AMD \CMD\CommonJS
 
主要有两个Javascript库实现了AMD规范:require.jscurl.js
CMD规范:Sea.js
Commonjs规范:NodeJs 。
前面是规范,后面是实例。
 
规范是一种标准,比如新能源汽车:新能源汽车是指采用非常规的车用燃料作为动力来源(或使用常规的车用燃料、采用新型车载动力装置)。这种就是一个规范。但是具体到某个品牌的车就是实际例子。比如特斯拉就是按照新能源汽车的标准来设计的。

(价格:72.35-132.49万)

1.2    模块规范
目前有三种流行的模块规范,分别是AMD、CMD、Commonjs(NodeJs)
 
http://blog.chinaunix.net/uid-26672038-id-4112229.html
http://www.zhihu.com/question/20351507/answer/14859415
 
JS中的模块有共性,模块的功能代码都是在一个函数中。
 
1、模块中使用var定义变量都是局部变量。
2、模块定义在函数,也是局部。
3、模块有一个模块对象。包含moduleId(模块名)、exports(导出对象)
4、如果模块中需要暴露方法或属性给外部使用,那么就执行往exports对象上面添加。
5、使用一个模块用require(“moduleId”),该方法返回的是模块对象的exports对象。
var a = require(“./a.js”);
a.xxx // 等同于模块中exports.xxx.
1.2.1 AMD
AMD, (Asynchronous Module Definition=异步模块定义),这种规范是异步的加载模块,requirejs应用了这一规范,适合客户端浏览器环境。
    AMD规范定义了一个自由变量或者说是全局变量 define 的函数。
    define( id?, dependencies?, factory );   
    
第一个参数 id 为字符串类型,表示了模块标识,为可选参数。若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。如果存在,那么模块标识必须为顶层的或者一个绝对的标识。
第二个参数,dependencies ,是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。
第三个参数,factory,是一个需要进行实例化的函数或者一个对象。
 
define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
    export.verb = function(){
        return beta.verb();
        // or:
        return require("beta").verb();
    }
});
 
RequireJS 是一个前端的模块化管理的工具库,遵循AMD规范,它的作者就是AMD规范的创始人 James Burke。RequireJS是对AMD规范的具体实现。
 

http://www.requirejs.cn/
 
Index.html :
 
<!DOCTYPE html>
<html>
    <head>
       <meta charset="utf-8" />
       <title></title>
    </head>
    <body>
       <!--
      
       1、async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。
加载require.js以后,下一步就要加载我们自己的代码了。
 
       2、data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。
 
       3、加载这个文件,也可能造成网页失去响应。解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:
       -->
       <script src="js/require.js" data-main="js/main" type="text/javascript" defer="defer" async="async" charset="utf-8"></script>
      
    </body>
</html>

 
main.js
 
require.config({
           baseUrl: "js",
           paths: {
             "jquery": "jquery-1.11.0",
                  'bbc':'moduleB'
           }
});
 
require(['moduleA', 'moduleB'], function (moduleA, moduleB){
    var c=moduleA.add(1,2);
       moduleB.show(c);
});
moduleA.js
 
define(function (){
    var add = function (x,y){
      return x+y;
    };
    return {
      add: add
    };
});
 
moduleB.js
 
define(function (){
    var show = function (c){
      console.log(c);
    };
    return {
      show: show
    };
});
 
 
1.2.2 CMD
CMD, (Common Module Definition), 是seajs推崇的规范,国内大牛玉伯之作。
Seajs被很多网友吐槽,文档不全,路径问题等。
 
define(factory);
factory,是一个需要进行实例化的函数或者一个对象。
 
define( function(require, exports, module) {
    // 模块代码
});
 
CMD推崇依赖就近,AMD推崇依赖前置。
 
 
//AMD
define(['./a','./b'], function (a, b) {
    //依赖一开始就写好
    a.test();
    b.test();
});
 
//CMD
define(function (requie, exports, module) {
    //依赖可以就近书写
    var a = require('./a');
    a.test();    
    //软依赖
    if (status) {
     
        var b = requie('./b');
        b.test();
    }
});
 
 
1.2.3 CommonJS
CommonJS, 是诞生比较早的。NodeJS就采用了CommonJS的规范来定义模块。但是CommonJs采用的是同步加载文件方式,只适用于服务端(NodeJs平台)
 
虽然有三种规范,写法也不同,但是规范的最终目的都是为了功能的“模块化”,将某个功能和多个功能独立与一个文件中,后期方便我们使用这些功能,而且模块与模块之间的是独立的,互不影响。
 
JavaScript是一个强大面向对象语言,它有很多快速高效的解释器。官方JavaScript标准定义的API是为了构建基于浏览器的应用程序。然而,并没有定于一个用于更广泛的应用程序的标准库。
 
CommonJs规范的出发点:JS没有模块系统、标准库较少、缺乏包管理工具;为了让JS可以在任何地方运行,以达到Java、C#、PHP这些后台语言具备开发大型应用的能力;
 
CommonJS API:不是一个语言,也不是一个平台,它只是一个标准。 它定义的标准主要是对JavaScript标准API进行增强。
 
CommonJS API定义很多普通应用程序(主要指非浏览器的应用)使用的API,从而填补了这个空白。它的终极目标是提供一个类似Python,Ruby和Java标准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用可以运行在不同的JavaScript解释器和不同的主机环境中。在兼容CommonJS的系统中,你可以实用JavaScript程序开发:
 
服务器端JavaScript应用程序
命令行工具
图形界面应用程序
混合应用程序(如,Titanium或Adobe AIR)
 
CommonJS是一种规范,NodeJS是这种规范的实现。
二进制:二进制(0,1)数据对象(字节数组和/或字符串)(建议、讨论、早期实现)
编码:编码和字符集(建议、讨论、早期实现) UTF-8,GBK
io:I / O流(建议、讨论)
fs,fs基地:文件系统(建议、讨论、早期实现)
系统:系统接口(stdin、stdout,stderr,等等)(1.0,提出修正案)
断言,测试:单元测试(1.0,修正案提议未决)
套接字:套接字I / O TCP / IP套接字(早期的建议)
事件队列:反应堆反应堆/事件队列(早期的建议)
工人:工(并发无共享进程/线程)(建议)
控制台:控制台(建议)
 
简单理解:Commonjs是对JavaScript的功能补充定义了规范(应新增那些功能),NodeJs是对CommonJs的实现(提供那些新功能)。
 
 
1.3    自定义模块
一个JS文件就是一个模块,所以定义一个模块,只需要新建一个JS文件即可。
 
模块的内部既可以有方法,一可以有属性。
 
hello.js
 
function sayHi(){
  console.log(“hi”);
}
 
function sayHello(){
  console.log(“hello”);
}
 
虽然我们在js定义了两个方法,但是这个两个方法只能在当前模块中使用,因为模块是独立的(内部的变量和函数只能在该模块使用),所以我们可以使用模块内的exports对象(导出对象)输出模块中功能供外部使用。
 
hello.js
 
function sayHi(){
  console.log(“hi”);
}
 
function sayHello(){
  console.log(“hello”);
}
 
//非常重要,导出模块功能(供外部使用)
exports.sayHi = sayHi;
exports.sayHello= sayHello;
 
1.4    使用模块
一个模块,一旦定义完成后,我们就可以使用这些模块。首先,你需要编写一个新的文件(模块),来使用hello.js模块。
 
使用模块,通过require(“模块”);函数
 
main.js
 
//引入模块
var hello = require(“./hello.js”); // .js可以省略
 
//使用模块中的功能
hello.sayHi();
hello.sayHello();
 
 
node main.js

1.5    主模块
通过命令行参数传递给NodeJS以启动程序的模块被称为主模块。主模块负责调
度组成整个程序的其它模块完成工作。例如通过以下命令启动程序
时,main.js就是主模块。
 
a.js   子模块
 
exports.msg = "这是一个简单模块";
 
exports.sayMsg = function(){
    console.log(this.msg); // this指向是exports对象。
}

//main 模块(主模块) :命令行参数传递给NodeJS以启动程序的模块被称为主模块。主模块一般在一个应用中只有一个。
//主模块只是用来引用其他模块的,一般不导出。(主模块负责调度组成整个程序的其它模块完成工作)
 
main.js 主模块
 
var s = require("./simple");//导入模块,require返回的是模块对象中exports导出对象。
console.log(s.msg);
s.sayMsg();
 
 

web前端ruitiancnweb前端z_vae@sina.com

web前端