一、模块
1.一个Node.js文件就是一个模块,这个文件可能 是JavaScript代码、JSON或者编译过的C/C++扩展。
2.Node.js提供了exports和require两个对象,其中exports是模块公开的接口,require用于从外部获取一个模块的接口,即所获取模块的exports对象。
3.一个文件就是一个模块。
第一个例子,如何创建一个模块,先创建一个module.js文件:
var name;
exports.setName = function(thyName){
name = thyName;
};
exports.sayHello = function(){
console.log('Hello' + name);
};
再在同一个目录创建getmodule.js
var myModule = require('./module');
myModule.setName('BYVoid');
myModule.sayHello();
最后cmd进入该目录运行node getmodule.js即可
原理:module.js通过exports对象把setName和sayHello作为模块的访问接口,在getmodule.js中通过require('./module')加载这个模块,然后就直接访问module.js中的exports对象的成员函数了。
延伸:因为require不会重复加载模块,也就是所无论调用多少次require获得的模块都是同一个。例如修改getmodule.js看效果:
var myModule1 = require('./module');
myModule1.setName('BYVoid');
var myModule2 = require('./module');
myModule2.setName('BYVoid2');
myModule1.sayHello();
运行结果为:
原理:因为变量myModule1和myModule2都是指向同一个实例,因此myModule1.setName的结果被myModule2.setName覆盖了。
第二个例子,覆盖exports,把一个对象封装到模块中:
新建一个singleobject.js
function Hello(){
var name;
this.setName = function(thyName){
name = thyName;
};
this.sayHello = function(){
console.log('HelloSingle' + name);
};
};
exports.Hello = Hello;
新建一个hello.js
function Hello(){
var name;
this.setName = function(thyName){
name = thyName;
};
this.sayHello = function(){
console.log('HelloHello' + name);
};
};
module.exports = Hello;
再新建一个gethello.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
运行结果是:
模块接口唯一变化是使用module.exports=Hello代替了exports.Hello=Hello。
exports本身仅仅是一个普通的空对象,即{},它专门用来声明接口,本质上是通过它为模块闭包的内部建立了一个有限的访问接口。因为它没有任何特殊的地方,所以可以用其他东西来替代,譬如上面例子中的Hello对象。
二、包
包:将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制。
Node.js对包只要顶层目录下有package.json,并符合一些规范即可。
模块与文件是一一对应的,最简单的包就是一个作为文件夹的模块。
建立一个例子说明作为文件夹的模块:
新建一个文件夹somepackage在其中创建index.js
exports.hello = function(){
console.log('Hello.');
};
然后在somepackage文件夹之外建立getpackage.js
var somePackage = require('./somepackage');
somePackage.hello();
cmd进入存放getpackage.js地址,node getpackage.js
原理:这种方法可以把文件夹封装为一个模块,即所谓的包。包通常是一些模块的集合,在模块的基础上提供了更高层的抽象,相当于提供一些固定接口的函数库。
通过定制package.json,可以创建更复杂、更完善、更符合规范的包用于发布,建立一个例子说明如下:
在somepackage文件夹下面,新创建一个叫做package.json的文件
{
"main" : "./lib/interface.js"
}
再在这个文件夹目录下面新建一个lib文件夹和在下面新建一个interface.js
exports.hello = function(){
console.log('HelloLib');
};
cmd进入存放getpackage.js地址,node getpackage.js
Node.js在调用某个包时,会首先检查包中package.json文件的main字段,将其作为包的接口模块,如果package.json或main字段不存在,会尝试寻找index.js或index.node作为包的接口。【如果既没有在package.json里面说明又没有在index.js或index.node中,就会报找不到此文件的错误哦】
三、包管理器npm
1.获取一个包:npm install/i package_name比如:npm install express或 npm i express。npm默认情况会从http://npmjs.org搜索下载包,将包安装到当前目录的node_modules子目录下面
2.本地模式和全局模式
(1)如果把包安装到全局,可以提高程序的重复利用程度,避免同样的内容多份副本,但坏处是难以处理不同版本依赖
如果把包安装到当前目录,或者说本地,则不会有不同程序依赖不同版本的包的冲突问题,同时减轻了包作者API兼容性压力,但缺陷是同一个包可能会被安装很多次。
(2)全局模式获取一个包:npm install/i -g package_name
多数情况下并不是因为许多程序都可能用到它,为了减少多重副本而使用全局模式,而是因为本地模式不会注册PATH环境变量。npm本地模式仅仅是把包安装到node_modules子目录下,其中的bin目录没有包含到PATH环境变量中,不能直接在命令行中调用。而全局模式下的supervisor.js就可以直接在命令行中运行supervisor XXX.js即可监控代码改动了。