nodeJS入门例子一—模块(Modules)

    模块(Modules)
        周期(Cycles)
        核心模块(Core Modules)
       文件模块( File Modules)
       从节点模块(node_modules)文件夹加在
        文件夹作为模块
        缓存(Caching)
           模块缓存说明
        module.exports
        module.require
        其他...
       从全局文件夹加载
       访问主模块

    附录: 包管理技巧


Modules(模块)

Node有一个简单的模块加载系统。在Node中,文件和模块 inone-to-one  通讯。如下例子,foo.js在相同目录下加载模块circle.js


 foo.js内容如下:

var circle = require('./circle.js');
console.log( 'The area of a circle of radius 4 is '
           + circle.area(4));

circle.js内容如下:

var PI = Math.PI;

exports.area = function (r) {
  return PI * r * r;
};

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

 circle.js模块有两个输出函数area()circumference()。为输出对象, 添加详细的输出对象(exports object)。

模块的局部变量是私有的。在这个例子中标量PI是circle.js私有的。


Cycles (周期)

当互相加载时,当期返回时一个模块可能不会执行完成

考虑这种情况:


a.js:

console.log('a starting');
exports.done = false;
var b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

b.js:

console.log('b starting');
exports.done = false;
var a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

main.js:

console.log('main starting');
var a = require('./a.js');
var b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

当main.js家在a.js时,a.js加载b.js,此时b.js尝试加载a.js。为了预防无限循环拷贝未完成的返回给b.js的a.js的输出对象。
b.js完成加载,并且将其输出对象提供给a.js模块


此时main.js完成加载两个模块。其输出如下

$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
如果在你程序中有循环依赖模块中,确保规划中间的关系。

Core Modules (核心模块)

Node有几个编译成二进制的模块。这些模块在NODE文档中有详尽的描述。

核心模块的定义在node的源码文件夹lib/文件夹中。


如果require()鉴定通过,核心模块将优先加载。例如,require("http")将总是返回建立的http模块,即使有一个文件的名字。


File Modules (文件模块)

如果执行的文件名没有找到,node将尝试加载请求后缀为.js、.json和.node    文件名相同的文件。.js文件解析成JavaScript的文本文件,

.json文件解析成JSON文本文件,.node文件解析成用dlopen加载的编译的附加模块

模块前缀为"/"是一个文件的绝对路径。如:require('/home/marco/foo.js')将加载在/home/marco/foo.js下的文件。

模块前缀为“./”是一个调用require()加载的文件的相对路径。也就是说,用require('./circle')加载时找到此文件,circle.js必须要和foo.js在同一目录下面。

没有'/'或者'./'前缀表示文件时,模块是核心模块或者是从node_modules文件夹里加载。


从`node_modules`文件夹中加载

如果通过require()标识模块不是本地模块,并且没有以'/'、'../'或者'./'开头,node从当前模块的父目录开始,添加/node_modules,并尝试从本地加载模块。

如果没有在这里找到,将会从父文件夹查找,直到达到最顶层文件夹。

例如,如果 '/home/ry/projects/foo.js'调用require('bar.js'),node将以以下本地路径查找,顺序如下:

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

这允许程序设置依赖的本地化,所以不会引起冲突。


文件夹作为模块(Folders as Modules )

这非常方便地组织程序和libraries    到一个独立的目录下,并且对library提供一个简单的入口点。在某个文件夹有三种方式作为参数传递给require()。

第一种在文件夹的根路径下(在main 模块中申明描述的)创建一个package.json文件。一个package.json文件的例子如下:


{ "name" : "some-library",
  "main" : "./lib/some-library.js" }

如果实在 ./some-library的文件夹下,require('./some-library')尝试着加载。./some-library/lib/some-library.js。
这是package.json文件的Node意识扩展。
如果在目录中没有package.json存在,node将尝试着加载文件夹外的index.js或者index.node文件。例如:上例子中没有package.json文件,
然后 require('./some-library')尝试着加载
  • ./some-library/index.js
  • ./some-library/index.node


缓存 (Caching )

当第一次加载时模块都将被缓存。也就是说如果在相同的文件中,每次调用 require('foo')将精确地获取返回的相同的对象,

多次调用require("foo")可能不会引起多次调用。这是一个很只要的特征。随之,部分完成对象可以返回,因此即时互调时都可依靠加载来传递。

如果想要模块代码多次调用,需要外置(export)一个函数,并调用此函数。


模块缓存说明(Module Caching Caveats )

模块的缓存基于决定的文件名。因为模块基于调用模块本地解析可能使用不同的文件名(从node_modules文件夹中加载)。如果不同的文件解析,不保证require("foo")将会总是返回准确相同的对象。


模块外置接口(module.exports )

exports对象是通过系统模块创建的。有时这不是合要求的,很多时候需要一些类的实体。为了做此分配,描述module.exports的export对象。例如建立一个叫a.js的模块(module)。


var EventEmitter = require('events').EventEmitter;

module.exports = new EventEmitter();

//一段时间后从这个模块本身触发'ready'事件。
setTimeout(function() {
  module.exports.emit('ready');
}, 1000);
在另外一个文件中,可以这么做:


var a = require('./a');
a.on('ready', function() {
  console.log('module a is ready');
});
注意分配module.exports必须立即完成。不能在任何回调函数中完成。如下就不能生效:


x.js:

setTimeout(function() {
  module.exports = { a: "hello" };
}, 0);

y.js:

var x = require('./x');
console.log(x.a);

module.require 

module.require方法提供一个加载模块的方法,就如从原始的module中调用require()。

为了做这些,必须获取module对象的引用。require()返回exports,module仅在模块范围内可用,为了使用必须明确外置(export)。


总结

当require()被调用时,为获取加载需要的精确 的文件名,使用require.resolve()函数。

将上述所有综合,这是高等级(high-level)需要请求的伪代码(pseudocode)算法(algorithmin )。

如下:


停止require(X) from module at path Y

在路径Y上从模块中请求X
1、如果X是代码模块
   a.返回代码模块。
   b.停止

2、如果X以'./' 或者 '/' 或者 '../'开头。
   a. 以文件加载(LOAD_AS_FILE(Y + X)) 
   b. 以目录加载(LOAD_AS_DIRECTORY(Y + X))
3、加载节点模块(LOAD_NODE_MODULE(X, dirname(Y)))
4、抛出未找到异常"not found"。


作为文件加载 LOAD_AS_FILE(X)

1、如果X是一个文件,以JavaScript文本加载X。停止
2、如果X.js是一个文件,以JavaScript文本加载X。
3、如果X.node是一个文件,以二进制插件加载X.node。停止


作为目录加载  LOAD_AS_DIRECTORY(X)
1、如果X/package.json是一个文件,
a.解析X/package.json,并查找主要域('main' field)
b. 设置M = X + json主域(json main field)
c. 加载文件M
2、如果X/index.js是一个文件, 作为JavaScript文本加载X/inde.js。停止。
3、如果X/index.node是一个文件,作为一个二进制插件加载,停止。


加载节点模块  LOAD_NODE_MODULES(X, START)
1、设置DIRS = NODE_MODULES_PATHS(START)。
2、对于目录下的每个目录
a.加载文件(DIR/X) LOAD_AS_FILE(DIR/X)
b.加载目录(DIR/X) LOAD_DIRECTORY(DIR/X)

 NODE_MODULES_PATHS(START)
1、设置PARTS = path split(START)
2、设置ROOT = PARTS中‘node_modules’的第一个实体的索引。
3、设置I = PARTS - 1
4、设置DIRS = []
5、while I > ROOT
a. 如果PARTS[I] = "node_modules" CONTINUE
c.DIR = (PARTS[0...I]+ 'node_modules'的联合路径)
b.DIRS = DIRS + DIR
c.设置I = I - 1 1. let PARTS = path split(START)
6、返回DIRS

从全局文件夹中加载 Loading from the global folders

如果NODE_PATH环境变量设置成冒号分割(colon-delimited)的绝对路径的集合,如果在任何地方都没找到,node将会为模块查找这些目录。

(注意:在Windows下,NODE_PATH分割是用分号代替冒号)

此外,node还会搜索一下位置:

1. $HOME/.node_modules

2. $HOME/.node_libraries

3. $PREFIX/lib/node


$HOME是用户home目录,$PREFIX是节点配置的安装前缀(installPrefix)。

这很具有历史意义。非常建议将依赖文件或包放置在node_modules文件夹中,这将会被快速加载,并且可靠。

访问主模块  Accessing the main module

当一个文件直接在node中运行时,require.main被这只到module。意思就是说你可以决定一个文件在测试时是否已经被直接运行。

require.main === module

对于文件foo.js,如果执行node foo.js 将为true。但通过require('./foo')时运行时为false。

因为module提供一个文件名属性(一般情况下等于_filename),当前应用的入口点可以通过检查require.main.filename获得。


附录:包管理技巧( Package Manager Tips)

节点的语义require()函数被设计成一般足以支持一些健全的目录结构。包管理程序(Package manager )如:dpkg,rpm,和npm将有希望从node模块中不需要修改创建本地包。

下面给出目录结构的建议:

在/usr/node/<some-package>/<some-version>目录下面放置一个包的详细版本说明。

包能够以来其它的。为了装载foo包,你必须装载包bar的版本说明。包bar有自己的依赖,在一些情况下,他们的依赖包可能形成碰撞,或互调循环。

因为node查找任何模块加载的真实路径,并在node_modules文件夹中根据上面的描述查找他们的依赖包,用下面的架构设计很容易解决尚需问题:

  • /usr/lib/node/foo/1.2.3/ - foo包的详细内容, 版本 1.2.3.
  • /usr/lib/node/bar/4.3.2/ - foo依赖的bar的详细说明
  • /usr/lib/node/foo/1.2.3/node_modules/bar - 象征连接到/usr/lib/node/bar/4.3.2/.
  • /usr/lib/node/bar/4.3.2/node_modules/* - bar依赖的象征连接到的包

因此,即使遇到循环互调,或者依赖包冲突,每个模块将可以给出其所用到的依赖包版本。

foo包中代码使用requirt("bar"),将给出版本连接到/usr/lib/node/foo/1.2.3/node_modules/bar。然后在bar包的代码中调用reuire(''quux),将给出版本连接到/usr/lib/node/bar/4.3.2/node_module/quux。

此外,为使的模块最佳检查process,不是把包直接放置在/usr/lib/node下,可以将其放置在/usr/lib/node_moduls/<name>/<version>。node将不会再/usr/node_modules或/node_modules.查找缺失的依赖包。

为使得模块对nod  REPL可用,将/usr/lib/node_modules文件夹设置到$NODE_PATH的环境变量下可能很有用。因为模块检查使用的node_mudules文件夹都是相对路径,

并且基于真实路径的文件调用require(),所以包可以在任何地方。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值