18
Feb 2014
Node

温习Node基础(1)——模块

字数:2702

作者:Jerry

了解Node的人一定都知道NPM——node的包管理器。NPM是基于CommonJS定义的包规范,实现了依赖管理和模块的自动安装等功能。

本篇文章从简单的模块定义与使用开始,逐步讲解Node中模块怎么定义,怎么require模块,模块require的机制等等。讲的东西很浅显,但却很重要。

简单模块的定义和使用

以一个例子开始:

  1. 新建文件夹SimpleModule,进入该文件夹,新建两个文件:circle.jsapp.js

    即如:

    ----SimpleModule
        |----app.js
        |----circle.js
    
  2. circle.js内容:

    var PI = Math.PI;
    exports.area = function(r){
        return PI * r * r;
    };
    exports.circumference = function(r){
        return 2 * PI *r;
    }
    

    circle.js就是简单的模块,功能比较简单,就是计算圆周和圆面积。

  3. app.js:

    // 引入circle.js
    var circle = require('./circle.js');
    console.log('Circle area (radius=4): '+circle.area(4));
    

    可以看到模块调用很方便,require模块所对应的文件即可。

可以看到,Node中模块的定义与调用均很简单。

注意:

模块载入策略

上面的例子中,我们通过require来载入了自己定义的模块,接下来将系统的了解模块的载入策略。

原生模块和文件模块

Nodejs的模块分为两类:原生(核心)模块和文件模块:

node对原生模块和文件模块都进行了缓存,所以第二次require时,不会有重复开销。

文件模块分为3类

以后缀区分,文件模块有分为3类,nodejs会根据后缀名来决定加载方法。

具体的加载方法我们暂时不去深入,我们只要知道,加载方法加载module后,返回module的exports对象。像我们的circle.js模块,加载后暴露了exports对象,因此我们可以调用exports.areaexports.circumference方法。

require方法的文件查找策略

Node有4类模块(原生模块和3种文件模块),加载优先级各不相同。

模块加载顺序:
  1. 从文件模块缓存中加载。

    require时,首先查看文件模块缓存,如果缓存中存在,直接返回exports对象。

  2. 其次从原生模块加载。

    原生模块也有缓存,同样优先从缓存区加载。如果缓存区没有被加载过,调用原生模块的加载方式进行加载和执行。

  3. 从文件加载。

    当文件模块缓存中不存在,而且也不是原生模块时,Node会解析require方法传入的参数,并从文件系统中加载实际的文件。

require方法接受4种参数:

包结构

Javascript本身是没有包结构的,CommonJS定义了包结构的规范(http://wiki.commonjs.org/wiki/Packages/1.0)。

一个符合CommonJS规范的包应该是如下结构:

package.json定义了一系列必须字段,以下只讲几个主要的:

如果你的包符合CommonJS规范,那么你可以通过NPM发布你的包。

npm publish <folder>

Nodejs模块与前端模块的异同

通常一些模块可以同时适用于前后端,但是浏览器通过<script></script>标签载入js文件的方式与node不同。

node在载入到最终执行过程中,进行了包装,使得每个js文件中的变量天然地形成在一个闭包中,不会污染全局变量。


来看jQuery源码的最后一段来加深对这种不同之处的理解:

(function( window, undefined ) {

    // 大量代码
    ...


    if ( typeof module === "object" && module && typeof module.exports === "object" ) {
        // CommonJS规范
        module.exports = jQuery;
    } else {
        // AMD规范
        if ( typeof define === "function" && define.amd ) {
            define( "jquery", [], function () { return jQuery; } );
        }
    }

    if ( typeof window === "object" && typeof window.document === "object" ) {
        window.jQuery = window.$ = jQuery;
    }

})( window );

详细从jQuery源码可以看出Nodejs模块与前端模块的差别之处了。前端模块下,jQuery通过为window全局对象添加属性完成暴露,而Nodejs下则是为module.exports赋值完成暴露。