您的位置:首页 > Web前端 > Node.js

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

2011-11-27 13:54 351 查看
模块(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
如果在你程序中有循环依赖模块中,确保规划中间的关系。

[/code]

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')
尝试着加载
[/code]
./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_modules2. $HOME/.node_libraries3. $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(),所以包可以在任何地方。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: