什么是模块
模块是Node.js应用程序的基本组成部分,文件和模块是一一对应的,也就是说,一个Node.js文件就是一个模块,这个文件可能是javascript代码、JSON或者编译过的C/C++扩展。之前我们了解过var http =require(‘http’); ,其中http是Node.js的一个核心模块,其内部是用C++实现的,外部用javascript封装。我们通过require函数获取了这个模块,然后能使用其中的对象
创建及加载模块
创建模块
在Node.js中,创建一个模块非常简单,因数一个文件就是一个模块,我们要关注的问题仅仅在于如何在其他文件中获取这个模块。Node.js提供了exports和requrie两个对象,其中exports是模块公开的接口,require用于从外部获取一个模块的接口,即所获取模块的exports对象。例:
//创建model.js
var name;
exports.setName =function(thyName){
name = thyName;
}
exprots.sayHello =function(){
console.log(‘Hello ‘+name);
}
在同一目录下创建getmodule.js内容是:
//getmodule.js
var myModule =require(‘./module’);
myModule.setName(‘WillZhao’);
myModule.sayHello();
node getmodule.js 结果是:Hello Will Zhao
单次加载
上面这个例子有点类似创建一个对象,但实际上和对象又有本质的区别,因为requrie不会重复加载模块,也就是说无论调用多少次require,获得的模块都是同一个。我们在getmodule.js的基础上稍作修改:
//loadmodule.js
var hello1 =require(‘./module’);
hello1.setName(“willzhao”);
var hello2 =require(“./module”);
hello2.setName(“Bigperson “);
hello1.sayHello();
//将打印Big person字符串
这是因为hello1和hello2指向的是同一个实例,因此hello1.setName的结果被hello2.setName覆盖,最终输出的结果是同后者决定的
覆盖exports
有时候我们只是想把一个对象封装到模块中,例如:
//singleobject.js
function Hello(){
var name;
this.setName = function(thyName){
name = thyName;
};
this.sayHello = function(){
console.log(‘Hello ’+name);
};
}
exports.Hello =Hello;
此时我们在其他文件中需要通过require(“./singleobject”).Hello来获取Hello对象,这略显冗余,可以用下面的方法稍微简化:
//hello.js
function Hello(){
var name;
this.setName = function(thyName){
name = thyName;
};
this.sayHello = function(){
console.log(‘Hello ’+name);
};
}
module.exports = Hello;
这样可以直接获取这个对象:
//gethello.js
var Hello =require(“./hello”);
hello = newHello();
hello.setName(“WillZhao”);
hello.sayHello();
注意模块接口的唯一变化是使用module.exports = Hello代替了exports.Hello=Hello。在外部引用该模块时,其接口对象就是要输出Hello对象本身,而不是原先的exports。事实上exports本身仅仅是一个普通的空对象,即{},它专门用来声明接口,本质上通过它为模块闭包的内部建立一个有限的访问接口。因为它没有任何特殊的地方,所以可以用其他东西代替,譬如我们上面例子中的Hello对象。(不可以通过对exports直接赋值代替对module.exports赋值。exports实际上只是一个和module.exports指向同一个对象的变量它本身会在模块执行结束后释放,但module不会,因此只能通过指定module.exprots来改变访问接口)
引:那到底Module.exports是什么呢?它是否合法呢?
其实,Module.exports
才是真正的接口,exports只不过是它的一个辅助工具。 最终返回给调用的是Module.exports
而不是exports。
所有的exports收集到的属性和方法,都赋值给了Module.exports
。当然,这有个前提,就是Module.exports本身不具备任何属性和方法。如果,Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。
创建包
包是在模块基础上更深一步的抽象,Node.js的包类似于C/C++的函数库或者java/.Net的类库。它将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制。Node.js根据CommonJS规范实现了包机制,开发了npm来解决包的发布和获取需求。Node.js的包是一个目录,其中包含一个JSON格式的包说明文件package.json。严格符合CommonJS规范的包应该具备以下特征:
n package.json必须在包的顶层目录下
n 二进制文件应该在bin目录下
n JavaScript代码应该在lib目录下
n 文档应该在doc目录下
n 单元测试应该在test目录下
Node.js对包的要求并没有这么严格,只要顶层目录下有package.json,并符合一些规范即可。当然为了提高兼容性,我们还是建议你在制作包的时候,严格遵守CommonJS规范。
1. 作为文件夹的模块
模块与文件是一一对应的。文件不仅可以是javascript代码或二进制代码,还可以是一个文件夹。最简单的包,就是一个作为文件夹的模块。下面我们来看一个例子,建立 一个叫做somepackage的文件夹,在其中创建index.js内容如下:
//somepackage/index.js
exports.hello=function(){
console.log(“hello.”);
}
然后在somepackage之外建立getpackage.js,内容如下:
//getpackage.js
var somePackage =require(“./somepackage”);
somePackage.hello();
运行node getpackage.js控制台上将输出结果Hello。
我们使用这种方法可以把文件夹封装为一个模块,即所谓的包。包通常是一些模块的集合,在模块的基础上提供了更高层的抽象,相当于提供了一些固定接口的函数库通过package.json我们可以创建更复杂的、更完善、更符合规范的包用于发布。
2. package.json
在前的例子的somepackage文件夹下,我们创建一个叫做package.json的文件,内容如下所示
{
“main”:”./lib/interface.js”
}
然后将index.js重命名为interface.js并放入lib子文件夹下。以同样的方式再次调用这个包,依然可以正常使用。Node.js在调用某个包时,会首先检查包中的package.json文件的main字段,将其作为包的接口。package.json是CommonJS规定的用来描述包的文件,完全符合规范的package.json文件应该含有以下字段
name:包名称,必须是唯一的,由小写英文字母、数字和下划线组成,不能包含空格
description:
version:
keywords:
maintainers: 每个元素要包含name、email(可选)、web(可选)
contributors:
bugs:
licenses:
repositories:仓库托管地址数组,每个元素要包含type,url,和path字段。
dependencies:包的依赖,一个关联数组,由包名称和版本号组成。
Node.js包管理器
Node.js包管理器,即npm是Node.js官方提供的包管理工具,它已经成了Node.js包的标准发布平台,用于Node.js包的发布、传播、依赖控制。npm提供了命令行工具,使你可以方便地下载、安装升级删除包,也可以让你作为开发者发布维护包。
1. 获取一个包
npm install/i [-g] [package_name]
如:npm install express 或 npm i express
此时epress就安装成功了,并且放置在当前目录的node_modules子目录下。npm在获取express的时候会将自动解析其依赖,并获取express依赖的mine/mkdirp/qs/connect
2. 本地模式和全局模式
npm默认情况下会从http://npmjs.org搜索或下载包,将包安装到当前目录的node_modules子目录下。为什么要使用全局模式呢?多数时候并不是因为许多程序都有可能用到它,为了减少多重副本而使用全局模式,而是因为本地模式不会没删PATH变量。
模式 | 可通过require使用 | 注册PATH |
本地模式 | 是 | 否 |
全局模式 | 否 | 是 |
3. 创建全局链接
npm提供了一个有趣的命令npm link,它的功能是在本地包和全局包之间创建符号链接。我们说过使用全局模式安装的包不能直接通过require使用,但通过npm link命令打破这一限制。如我们已经通过npm install –g express安装了express,这时在工程目录下运行命令:
npm link express
./node_module/express->/usr/local/lib/node_modules/express
我们可以在node_modules子目录中发现一个指向安装到全局的包的符号链接。通过这种方法,我们就可以把全局包当本地包来使用了。
npm link命令还可以将本地的包链接到全局。使用方法是在包目录中运行npm link命令
包发布
调式
node debug xxx.js
使用eclipse intellJ
使用node-inspector调用Node.js:
首先,使用npm install –g node-inspector命令安装node-inspector然后在终端中通过node –debug-brk=5858xxx.js 命令来连接你要除错的脚本的调试服务器,启动node-inspector:
$ node-inspector
在浏览器中打开:http://127.0.0.1:8080/debug?port=5858,即可显示出优雅的web调试工具。