Node.js是JavaScript在服务器端的一个运行环境,也是一个工具库,用来与服务器端其余软件互动。它的JavaScript解释器,采用了Google公司的V8引擎。javascript
访问官方网站nodejs.org了解安装细节。html
安装完成之后,运行下面的命令,查看是否能正常运行。java
node --version
// 或者
node -v
更新node.js版本,能够经过node.js的n模块完成。node
sudo npm install n -g
sudo n stable
上面代码经过n模块,将node.js更新为最新发布的稳定版。git
n模块也能够指定安装的版本。github
sudo n 0.8.21
若是想在同一台机器,同时运行多个版本的node.js,就须要用到版本管理工具nvm。shell
首先,须要安装nvm。数据库
git clone https://github.com/creationix/nvm.git ~/.nvm
而后使用下面的命令,激活nvm。express
source ~/.nvm/nvm.sh
上面这条命令,每次使用nvm前都要输入,建议将其加入~/.bashrc文件(假定你所使用的shell是bash)。npm
激活nvm以后,就能够安装指定版本的node.js。
nvm install 0.10
上面这条命令,安装最新的v0.10.x版本的node.js。
安装后,就能够指定使用该版本。
nvm use 0.10
或者,直接进入该版本的REPL环境。
nvm run 0.10
若是在项目根目录下新建一个.nvmrc文件,将版本号写入其中,则nvm use命令就再也不须要附加版本号。
nvm use
ls命令用于查看本地所安装的版本。
nvm ls
ls-remote命令用于查看服务器上全部可供安装的版本。
nvm ls-remote
若是要退出已经激活的nvm,使用deactivate命令。
nvm deactivate
安装完成后,运行node.js程序,就是使用node命令读取JavaScript脚本。
假定当前目录有一个demo.js的脚本文件,运行时这样写。
node demo
// 或者
node demo.js
在命令行键入node命令,后面没有文件名,就进入一个Node.js的REPL环境(Read–eval–print loop,"读取-求值-输出"循环),能够直接运行各类JavaScript命令。
node
> 1+1
2
若是使用参数 --use_strict,则REPL将在严格模式下运行。
node --use_strict
这个REPL是Node.js与用户互动的shell,各类基本的shell功能均可以在里面使用,好比使用上下方向键遍历曾经使用过的命令。特殊变量下划线(_)表示上一个命令的返回结果。
> 1+1
2
> _+1
3
在REPL中,若是运行一个表达式,会直接在命令行返回结果,若是运行一条语句则不会,由于它没有返回值。
> x = 1 1 > var x = 1
上面代码的第二条命令,没有显示任何结果。由于这是一条语句,不是表达式,因此没有返回值。
Node采用V8引擎处理JavaScript脚本,最大特色就是单线程运行,一次只能运行一个任务。这致使Node大量采用异步操做(asynchronous opertion),即任务不是立刻执行,而是插在任务队列的尾部,等到前面的任务运行完后再执行。
因为这种特性,某一个任务的后续操做,每每采用回调函数(callback)的形式进行定义。
var isTrue = function(value, callback) { if (value === true) { callback(null, "Value was true."); } else { callback(new Error("Value is not true!")); } }
上面代码就把进一步的处理,交给回调函数callback。约定俗成,callback的位置老是最后一个参数。值得注意的是,callback的格式也有约定。
var callback = function (error, value) { if (error) { return console.log(error); } console.log(value); }
callback的第一个参数是一个Error对象,第二个参数才是真正的数据。若是没有发生错误,第一个参数就传入null。这种写法有一个很大的好处,就是说只要判断回调函数的第一个参数,就知道有没有出错,若是不是null,就确定出错了。
Node提供如下一些全局对象,它们是全部模块均可以调用的。
全局函数:
全局变量:
除此以外,还有一些对象其实是模块内部的局部变量,指向的对象根据模块不一样而不一样,可是全部模块都适用,能够看做是伪全局变量,主要为module, module.exports, exports等。
module变量指代当前模块。module.exports变量表示当前模块对外输出的接口,其余文件加载该模块,实际上就是读取module.exports变量。
这里须要特别指出的是,exports变量其实是一个指向module.exports对象的连接,等同在每一个模块头部,有一行这样的命令。
var exports = module.exports;
这形成的结果是,在对外输出模块接口时,能够向exports对象添加方法,可是不能直接将exports变量指向一个函数。
exports = function (x){ console.log(x);};
上面这样的写法是无效的,由于它切断了exports与module.exports之间的连接。可是,下面这样写是能够的。
exports.area = function (r) { return Math.PI * r * r; }; exports.circumference = function (r) { return 2 * Math.PI * r; };
若是你以为,exports与module.exports之间的区别很难分清,一个简单的处理方法,就是放弃使用exports,只使用module.exports。
Node.js采用模块化结构,按照CommonJS规范定义和使用模块。模块与文件是一一对应关系,即加载一个模块,实际上就是加载对应的一个模块文件。
require方法用于指定加载模块。
var circle = require('./circle.js');
上面代码代表,从当前目录下的circle.js文件,加载circle模块。由于require方法默认加载的就是js文件,所以能够把js后缀名省略。
var circle = require('./circle');
require方法的参数是模块文件的名字。它分红两种状况,第一种状况是参数中含有文件路径(好比上例),这时路径是相对于当前脚本所在的目录,第二种状况是参数中不含有文件路径(好比下例)。
var bar = require('bar');
若是require方法的参数不带有路径,则node.js依次按照如下顺序,去寻找模块文件。
node.js依次到下面的目录,去寻找bar模块。
能够看到,若是没有指明模块所在位置,Node会依次从当前目录向上,一级级在node_modules子目录下寻找模块。若是没有找到该模块,会抛出一个错误。这样作的好处是,不一样的项目能够在本身的目录中,安装同一个模块的不一样版本,而不会发生版本冲突。
有时候,一个模块自己就是一个目录,目录中包含多个文件。这时候,Node在package.json文件中,寻找main属性所指明的模块入口文件。
{ "name" : "bar", "main" : "./lib/bar.js" }
上面代码中,模块的启动文件为lib子目录下的bar.js。当使用require('bar')命令加载该模块时,实际上加载的是bar/lib/some-library.js
文件。下面写法会起到一样效果。
var bar = require('bar/lib/bar.js')
若是模块目录中没有package.json文件,node.js会尝试在模块目录中寻找index.js或index.node文件进行加载。
模块一旦被加载之后,就会被系统缓存。若是第二次还加载该模块,则会返回缓存中的版本,这意味着模块实际上只会执行一次。若是但愿模块执行屡次,则可让模块返回一个函数,而后屡次调用该函数。
Node.js自带一系列的核心模块,下面就是其中的一部分:
这些模块能够不用安装就使用。
除了使用核心模块,还可使用第三方模块,以及自定义模块。
Node.js模块采用CommonJS规范。只要符合这个规范,就能够自定义模块。
下面是一个最简单的模块,假定新建一个foo.js文件,写入如下内容。
// foo.js module.exports = function(x) { console.log(x); };
上面代码就是一个模块,它经过module.exports变量,对外输出一个方法。
这个模块的使用方法以下。
// index.js var m = require('./foo'); m("这是自定义模块");
上面代码经过require命令加载模块文件foo.js(后缀名省略),将模块的对外接口输出到变量m,而后调用m。这时,在命令行下运行index.js,屏幕上就会输出“这是自定义模块”。
node index
# 这是自定义模块
module变量是整个模块文件的顶层变量,它的exports属性就是模块向外输出的接口。若是直接输出一个函数(就像上面的foo.js),那么调用模块就是调用一个函数。可是,模块也能够输出一个对象。下面对foo.js进行改写。
// foo.js var out = new Object(); function p(string) { console.log(string); } out.print = p; module.exports = out;
上面的代码表示模块输出out对象,该对象有一个print属性,指向一个函数。下面是这个模块的使用方法。
// index.js var m = require('./foo'); m.print("这是自定义模块");
上面代码表示,因为具体的方法定义在模块的print属性上,因此必须显式调用print属性。
fs是filesystem的缩写,该模块提供本地文件的读写能力。
(1)readfile方法
var fs = require('fs'); fs.readFile('example_log.txt', function (err, logData) { if (err) throw err; var text = logData.toString(); });
上面代码使用readFile方法读取文件。readFile方法的第一个参数是文件名,第二个参数是回调函数。这两个参数中间,还能够插入一个可选参数,表示文件的编码。
fs.readFile('example_log.txt', 'utf8', function (err, logData) { // ... });
可用的文件编码包括“ascii”、“utf8”和“base64”。若是这个参数没有提供,默认是utf8。
(2)readFileSync方法
若是想要同步读取文件,可使用readFileSync方法。
var data = fs.readFileSync('./file.json');
(3)writeFile方法
写入文件要使用writeFile方法。
fs.writeFile('./file.txt', data, function (err) { if (err) { console.log(err.message); return; } console.log('Saved successfully.'); });
(4)readdir方法
readdir方法用于读取目录,返回一个所包含的文件和子目录的数组。
fs.readdir(process.cwd(), function (err, files) { if (err) { console.log(err); return; } var count = files.length; var results = {}; files.forEach(function (filename) { fs.readFile(filename, function (data) { results[filename] = data; count--; if (count <= 0) { // 对全部文件进行处理 } }); }); });
(5)fs.exists(path, callback)
exists方法用来判断给定路径是否存在,而后无论结果如何,都会调用回调函数。
fs.exists('/path/to/file', function (exists) { util.debug(exists ? "it's there" : "no file!"); });
上面代码代表,回调函数的参数是一个表示文件是否存在的布尔值。
须要注意的是,不要在open方法以前调用exists方法,open方法自己就能检查文件是否存在。
下面的例子是若是给定目录存在,就删除它。
if(fs.exists(outputFolder)) { console.log("Removing "+outputFolder); fs.rmdir(outputFolder); }
Stream是数据处理的一种形式,能够用来取代回调函数。举例来讲,传统形式的文件处理,必须先将文件所有读入内存,而后调用回调函数,若是遇到大文件,整个过程将很是耗时。Stream则是将文件分红小块读入内存,每读入一次,都会触发相应的事件。只要监听这些事件,就能掌握进展,作出相应处理,这样就提升了性能。Node内部的不少IO处理都采用Stream,好比HTTP链接、文件读写、标准输入输出。
Stream是一个抽象接口,定义了readable、writable、drain、data、end、close等事件。它既能够读取数据,也能够写入数据。读写数据时,每读入(或写入)一段数据,就会触发一次data事件,所有读取(或写入)完毕,触发end事件。若是发生错误,则触发error事件。
fs模块的createReadStream方法用于新建读取数据流,createWriteStream方法用于新建写入数据流。使用这两个方法,能够作出一个用于文件复制的脚本copy.js。
// copy.js var fs = require('fs'); console.log(process.argv[2], '->', process.argv[3]); var readStream = fs.createReadStream(process.argv[2]); var writeStream = fs.createWriteStream(process.argv[3]); readStream.on('data', function (chunk) { writeStream.write(chunk); }); readStream.on('end', function () { writeStream.end(); }); readStream.on('error', function (err) { console.log("ERROR", err); }); writeStream.on('error', function (err) { console.log("ERROR", err); });
上面代码很是容易理解,使用的时候直接提供源文件路径和目标文件路径,就能够了。
node cp.js src.txt dest.txt
Streams对象都具备pipe方法,起到管道做用,将一个数据流输入另外一个数据流。因此,上面代码能够重写成下面这样:
var fs = require('fs'); console.log(process.argv[2], '->', process.argv[3]); var readStream = fs.createReadStream(process.argv[2]); var writeStream = fs.createWriteStream(process.argv[3]); readStream.on('open', function () { readStream.pipe(writeStream); }); readStream.on('end', function () { writeStream.end(); });
使用Node.js搭建HTTP服务器很是简单。
var http = require('http'); http.createServer(function (request, response){ response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello World\n'); }).listen(8080, "127.0.0.1"); console.log('Server running on port 8080.');
上面代码第一行 var http = require("http"),表示加载http模块。而后,调用http模块的createServer方法,创造一个服务器实例,将它赋给变量http。
ceateServer方法接受一个函数做为参数,该函数的req参数是一个对象,表示客户端的HTTP请求;res参数也是一个对象,表示服务器端的HTTP回应。rese.writeHead方法表示,服务器端回应一个HTTP头信息;response.end方法表示,服务器端回应的具体内容,以及回应完成后关闭本次对话。最后的listen(8080)表示启动服务器实例,监听本机的8080端口。
将上面这几行代码保存成文件app.js,而后用node调用这个文件,服务器就开始运行了。
node app.js
这时命令行窗口将显示一行提示“Server running at port 8080.”。打开浏览器,访问http://localhost:8080,网页显示“Hello world!”。
上面的例子是当场生成网页,也能够事前写好网页,存在文件中,而后利用fs模块读取网页文件,将其返回。
var http = require('http'); var fs = require('fs'); http.createServer(function (request, response){ fs.readFile('data.txt', function readData(err, data) { response.writeHead(200, {'Content-Type': 'text/plain'}); response.end(data); }); }).listen(8080, "127.0.0.1"); console.log('Server running on port 8080.');
下面的修改则是根据不一样网址的请求,显示不一样的内容,已经至关于作出一个网站的雏形了。
var http = require("http"); http.createServer(function(req, res) { // 主页 if (req.url == "/") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("Welcome to the homepage!"); } // About页面 else if (req.url == "/about") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("Welcome to the about page!"); } // 404错误 else { res.writeHead(404, { "Content-Type": "text/plain" }); res.end("404 error! File not found."); } }).listen(8080, "localhost");
回调函数的req(request)对象,拥有如下属性。
当客户端采用POST方法发送数据时,服务器端能够对data和end两个事件,设立监听函数。
var http = require('http'); http.createServer(function (req, res) { var content = ""; req.on('data', function (chunk) { content += chunk; }); req.on('end', function () { res.writeHead(200, {"Content-Type": "text/plain"}); res.write("You've sent: " + content); res.end(); }); }).listen(8080);
data事件会在数据接收过程当中,每收到一段数据就触发一次,接收到的数据被传入回调函数。end事件则是在全部数据接收完成后触发。
request方法用于发出HTTP请求。
var http = require('http'); //The url we want is: 'www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new' var options = { host: 'www.random.org', path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new' }; callback = function(response) { var str = ''; //another chunk of data has been recieved, so append it to `str` response.on('data', function (chunk) { str += chunk; }); //the whole response has been recieved, so we just print it out here response.on('end', function () { console.log(str); }); } var req = http.request(options, callback); req.write("hello world!"); req.end();
request对象的第一个参数是options对象,用于指定请求的域名和路径,第二个参数是请求完成后的回调函数。
若是使用POST方法发出请求,只需在options对象中设定便可。
var options = { host: 'www.example.com', path: '/', port: '80', method: 'POST' };
指定HTTP头信息,也是在options对象中设定。
var options = { headers: {'custom': 'Custom Header Demo works'} };
搭建HTTPs服务器须要有SSL证书。对于向公众提供服务的网站,SSL证书须要向证书颁发机构购买;对于自用的网站,能够自制。
自制SSL证书须要OpenSSL,具体命令以下。
openssl genrsa -out key.pem
openssl req -new -key key.pem -out csr.pem
openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem rm csr.pem
上面的命令生成两个文件:ert.pem(证书文件)和 key.pem(私钥文件)。有了这两个文件,就能够运行HTTPs服务器了。
var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem') }; var a = https.createServer(options, function (req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000);
上面代码显示,HTTPs服务器与HTTP服务器的最大区别,就是createServer方法多了一个options参数。运行之后,就能够测试是否可以正常访问。
curl -k https://localhost:8000
events模块是node.js对“发布/订阅”模式(publish/subscribe)的部署。也就说,经过events模块的EventEmitter属性,创建一个消息中心;而后经过on方法,为各类事件指定回调函数,从而将程序转为事件驱动型,各个模块之间经过事件联系。
var EventEmitter = require("events").EventEmitter; var ee = new EventEmitter(); ee.on("someEvent", function () { console.log("event has occured"); }); ee.emit("someEvent");
上面代码在加载events模块后,经过EventEmitter属性创建了一个EventEmitter对象实例,这个实例就是消息中心。而后,经过on方法为someEvent事件指定回调函数。最后,经过emit方法触发someEvent事件。
emit方法还接受第二个参数,用于向回调函数提供参数。
ee.on("someEvent", function (data){ console.log(data); }); ee.emit("someEvent", data);
默认状况下,Node.js容许同一个事件最多能够触发10个回调函数。
ee.on("someEvent", function () { console.log("event 1"); }); ee.on("someEvent", function () { console.log("event 2"); }); ee.on("someEvent", function () { console.log("event 3"); });
超过10个回调函数,会发出一个警告。这个门槛值能够经过setMaxListeners方法改变。
ee.setMaxListeners(20);
events模块的做用,还表示在其余模块能够继承这个模块,所以也就拥有了EventEmitter接口。
var util = require("util"); var EventEmitter = require("events").EventEmitter; function UserList (){ EventEmitter.call(this); } util.inherits(UserList, EventEmitter); UserList.prototype.save = function (obj) { // save into database this.emit("saved-user", obj); };
上面代码新建了一个构造函数UserList,而后让其继承EventEmitter,所以UserList就拥有了EventEmitter的接口。最后,为UserList的实例定义一个save方法,表示将数据储存进数据库,在储存完毕后,使用EventEmitter接口的emit方法,触发一个saved-user事件。使用的时候,监听saved-user事件便可。
var ul = new UserList(); ul.on('saved-user', function(data){ console.log('保存成功'); })
events模块默认支持一些事件。
ee.on("newListener", function (evtName){ console.log("New Listener: " + evtName); }); ee.on("removeListener", function (evtName){ console.log("Removed Listener: " + evtName); }); function foo (){} ee.on("save-user", foo); ee.removeListener("save-user", foo); // New Listener: removeListener // New Listener: save-user // Removed Listener: save-user
上面代码会触发两次newListener事件,以及一次removeListener事件。
(1)once方法
该方法相似于on方法,可是回调函数只触发一次。
ee.once("firstConnection", function (){ console.log("本提示只出现一次"); });
(2)removeListener方法
该方法用于移除回调函数。它接受两个参数,第一个是事件名称,第二个是回调函数名称。这就是说,不能用于移除匿名函数。
function onlyOnce () { console.log("You'll never see this again"); ee.removeListener("firstConnection", onlyOnce); } ee.on("firstConnection", onlyOnce);
上面代码起到与once方法相似效果。
(3)removeAllListeners方法
该方法用于移除某个事件的全部回调函数。
ee.removeAllListeners("firstConnection");
若是不带参数,则表示移除全部事件的全部回调函数。
ee.removeAllListeners();
(4)listener方法
该方法接受一个事件名称做为参数,返回该事件全部回调函数组成的数组。
function onlyOnce () { console.log(ee.listeners("firstConnection")); ee.removeListener("firstConnection", onlyOnce); console.log(ee.listeners("firstConnection")); } ee.on("firstConnection", onlyOnce) ee.emit("firstConnection"); ee.emit("firstConnection"); // [ [Function: onlyOnce] ] // []
上面代码显示两次回调函数组成的数组,第一次只有一个回调函数onlyOnce,第二次是一个空数组,由于removeListener方法取消了回调函数。
process模块用来与当前进程互动,能够经过全局变量process访问,没必要使用require命令加载。
process对象提供一系列属性,用于返回系统信息。
下面是主要属性的介绍。
(1)stdout
process.stdout用来控制标准输出,也就是在命令行窗口向用户显示内容。它的write方法等同于console.log。
exports.log = function() { process.stdout.write(format.apply(this, arguments) + '\n'); };
(2)argv
process.argv返回命令行脚本的各个参数组成的数组。
先新建一个脚本文件argv.js。
// argv.js console.log("argv: ",process.argv); console.log("argc: ",process.argc);
在命令行下调用这个脚本,会获得如下结果。
node argv.js a b c # [ 'node', '/path/to/argv.js', 'a', 'b', 'c' ]
上面代码表示,argv返回数组的成员依次是命令行的各个部分。要获得真正的参数部分,能够把argv.js改写成下面这样。
// argv.js var myArgs = process.argv.slice(2); console.log(myArgs);
process对象提供如下方法:
process.chdir()改变工做目录的例子。
process.cwd() # '/home/aaa' process.chdir('/home/bbb') process.cwd() # '/home/bbb'
process.nextTick()的例子,指定下次事件循环首先运行的任务。
process.nextTick(function () { console.log('Next event loop!'); });
上面代码能够用setTimeout改写,可是nextTick的效果更高、描述更准确。
setTimeout(function () { console.log('Next event loop!'); }, 0)
(1)exit事件
当前进程退出时,会触发exit事件,能够对该事件指定回调函数。
process.on('exit', function () { fs.writeFileSync('/tmp/myfile', 'This MUST be saved on exit.'); });
(2)uncaughtException事件
当前进程抛出一个没有被捕捉的意外时,会触发uncaughtException事件。
process.on('uncaughtException', function (err) { console.error('An uncaught error occurred!'); console.error(err.stack); });
Node.js默认单进程运行,对于多核CPU的计算机来讲,这样作效率很低,由于只有一个核在运行,其余核都在闲置。cluster模块就是为了解决这个问题而提出的。
cluster模块容许设立一个主进程和若干个worker进程,由主进程监控和协调worker进程的运行。
var cluster = require('cluster'); var os = require('os'); if (cluster.isMaster){ for (var i = 0, n = os.cpus().length; i < n; i += 1){ cluster.fork(); } }else{ http.createServer(function(req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000); }
上面代码先判断当前进程是否为主进程(cluster.isMaster),若是是的,就按照CPU的核数,新建若干个worker进程;若是不是,说明当前进程是worker进程,则在该进程启动一个服务器程序。
每一个项目的根目录下面,通常都有一个package.json文件,定义了这个项目所须要的各类模块,以及项目的配置信息(好比名称、版本、许可证等元数据)。npm install 命令根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。
下面是一个最简单的package.json文件,只定义两项元数据:项目名称和项目版本。
{ "name" : "xxx", "version" : "0.0.0", }
上面代码说明,package.json文件内部就是一个json对象,该对象的每个成员就是当前项目的一项设置。好比name就是项目名称,version是版本(遵照“大版本.次要版本.小版本”的格式)。
下面是一个更完整的package.json文件。
{ "name": "Hello World", "version": "0.0.1", "author": "张三", "description": "第一个node.js程序", "keywords":["node.js","javascript"], "repository": { "type": "git", "url": "https://path/to/url" }, "license":"MIT", "engines": {"node": "0.10.x"}, "bugs":{"url":"http://path/to/bug","email":"bug@example.com"}, "contributors":[{"name":"李四","email":"lisi@example.com"}], "scripts": { "start": "node index.js" }, "dependencies": { "express": "latest", "mongoose": "~3.8.3", "handlebars-runtime": "~1.0.12", "express3-handlebars": "~0.5.0", "MD5": "~1.2.0" }, "devDependencies": { "bower": "~1.2.8", "grunt": "~0.4.1", "grunt-contrib-concat": "~0.3.0", "grunt-contrib-jshint": "~0.7.2", "grunt-contrib-uglify": "~0.2.7", "grunt-contrib-clean": "~0.5.0", "browserify": "2.36.1", "grunt-browserify": "~1.3.0", } }
上面代码中,有些成员的含义很明显,但有几项须要解释一下。
(1)engines
engines指明了该项目所须要的node.js版本。
(2)scripts
scripts指定了运行脚本命令的npm命令行缩写,好比start指定了运行npm run start时,所要执行的命令。
下面的设置指定了npm run preinstall、npm run postinstall、npm run start、npm run test时,所要执行的命令。
"scripts": { "preinstall": "echo here it comes!", "postinstall": "echo there it goes!", "start": "node index.js", "test": "tap test/*.js" }
(3)dependencies,devDependencies
dependencies和devDependencies两项,分别指定了项目运行所依赖的模块、项目开发所须要的模块。
dependencies和devDependencies这两项,都指向一个对象。该对象的各个成员,分别由模块名和对应的版本要求组成。对应的版本能够加上各类限定,主要有如下几种:
package.json文件能够手工编写,也可使用npm init命令自动生成。
npm init
这个命令采用互动方式,要求用户回答一些问题,而后在当前目录生成一个基本的package.json文件。全部问题之中,只有项目名称(name)和项目版本(version)是必填的,其余都是选填的。
有了package.json文件,直接使用npm install命令,就会在当前目录中安装所须要的模块。
npm install
若是一个模块不在package.json文件之中,能够单独安装这个模块,并使用相应的参数,将其写入package.json文件之中。
npm install express --save
npm install express --save-dev
上面代码表示单独安装express模块,--save参数表示将该模块写入dependencies属性,--save-dev表示将该模块写入devDependencies属性。
npm有两层含义。一层含义是Node.js的开放式模块登记和管理系统,网址为http://npmjs.org。另外一层含义是Node.js默认的模块管理器,是一个命令行下的软件,用来安装和管理node模块。
npm不须要单独安装。在安装node的时候,会连带一块儿安装npm。node安装完成后,能够用下面的命令,查看一下npm的帮助文件。
# npm命令列表 npm help # 各个命令的简单用法 npm -l
下面的命令分别查看npm的版本和配置。
npm -version
npm config list -l
npm的版本能够在Node更新的时候一块儿更新。若是你想单独更新npm,使用下面的命令。
npm update -global npm
上面的命令之因此最后一个参数是npm,是由于npm自己也是Node.js的一个模块。
npm的info命令能够查看每一个模块的具体信息。好比,查看underscore模块信息的命令是:
npm info underscore
上面命令返回一个JavaScript对象,包含了underscore模块的详细信息。
{ name: 'underscore', description: 'JavaScript\'s functional programming helper library.', 'dist-tags': { latest: '1.5.2', stable: '1.5.2' }, repository: { type: 'git', url: 'git://github.com/jashkenas/underscore.git' }, homepage: 'http://underscorejs.org', main: 'underscore.js', version: '1.5.2', devDependencies: { phantomjs: '1.9.0-1' }, licenses: { type: 'MIT', url: 'https://raw.github.com/jashkenas/underscore/master/LICENSE' }, files: [ 'underscore.js', 'underscore-min.js', 'LICENSE' ], readmeFilename: 'README.md'}
上面这个JavaScript对象的每一个成员,均可以直接从info命令查询。
npm info underscore description
# JavaScript's functional programming helper library. npm info underscore homepage # http://underscorejs.org npm info underscore version # 1.5.2
每一个模块能够“全局安装”,也能够“本地安装”。二者的差别是模块的安装位置,以及调用方法。
“全局安装”指的是将一个模块直接下载到Node的安装目录中,各个项目均可以调用。“本地安装”指的是将一个模块下载到当前目录的node_modules子目录,而后只有在当前目录和它的子目录之中,才能调用这个模块。通常来讲,全局安装只适用于工具模块,好比npm和grunt。
默认状况下,npm install 命令是“本地安装”某个模块。
npm install [package name]
npm也支持直接输入github地址。
npm install git://github.com/package/path.git
npm install git://github.com/package/path.git#0.1.0
使用安装命令之后,模块文件将下载到当前目录的 node_modules 子目录。
使用global参数,能够“全局安装”某个模块。
sudo npm install -global [package name]
global参数能够被简化成g参数。
sudo npm install -g [package name]
install命令老是安装模块的最新版本,若是要安装模块的特定版本,能够在模块名后面加上@和版本号。
npm install package_name@version
一旦安装了某个模块,就能够在代码中用require命令调用这个模块。
var backbone = require('backbone') console.log(backbone.VERSION)
npm update 命令能够升级本地安装的模块。
npm update [package name]
加上global参数,能够升级全局安装的模块。
npm update -global [package name]
npm uninstall 命令,删除本地安装的模块。
npm uninstall [package name]
加上global参数,能够删除全局安装的模块。
sudo npm uninstall [package name] -global
npm list命令,默认列出当前目录安装的全部模块。若是使用global参数,就是列出全局安装的模块。
npm list
npm -global list
向服务器端搜索某个模块,使用search命令(可以使用正则搜索)。
npm search [搜索词]
若是不加搜索词,npm search 默认返回服务器端的全部模块。
在package.json文件有一项scripts,用于指定脚本命令,供npm直接调用。
"scripts": { "watch": "watchify client/main.js -o public/app.js -v", "build": "browserify client/main.js -o public/app.js", "start": "npm run watch & nodemon server.js", "test": "node test/all.js" },
上面代码在scripts项,定义了三个脚本命令,而且每一个命令有一个别名。使用的时候,在命令行键入npm run后面加上别名,就能调用相应的脚本命令。
npm run watch
npm run build
npm run start
npm run test
其中,start和test属于特殊命令,能够省略run。
npm start
npm test
通常来讲,每一个项目都会在项目目录内,安装所需的模块文件。也就是说,各个模块是局部安装。可是有时候,咱们但愿模块是一个符号连接,连到外部文件,这时候就须要用到npm link命令。
如今模块A(moduleA)的安装目录下运行npm link命令。
/path/to/moduleA $ npm link
上面的命令会在npm的安装目录内,生成一个符号连接文件。
/usr/local/share/npm/lib/node_modules/moduleA -> /path/to/moduleA
而后,转到你须要放置该模块的项目目录,再次运行npm link命令,并指定模块名。
/path/to/my-project $ npm link moduleA
上面命令等同于生成了本地模块的符号连接。
/path/to/my-project/node_modules/moduleA -> /usr/local/share/npm/lib/node_modules/moduleA
而后,就能够在你的项目中,加载该模块了。
require('moduleA')
若是你的项目再也不须要该模块,能够在项目目录内使用npm unlink命令,删除符号连接。
/path/to/my-project $ npm unlink moduleA
在发布你的模块以前,须要先设定我的信息。
npm set init.author.name "xxx" npm set init.author.email "xxx@gmail.com" npm set init.author.url "http://xxx.com"
而后,请npm系统申请用户名。
npm adduser
运行上面的命令以后,屏幕上会提示输入用户名,而后是输入Email地址和密码。
上面全部的这些我的信息,所有保存在~/.npmrc文件之中。
npm模块就是一个遵循CommonJS规范的JavaScript脚本文件。此外,在模块目录中还必须有一个提供自身信息的package.json文件,通常采用npm init命令生成这个文件。
npm init
运行上面的命令,会提示回答一系列问题,结束后自动生成package.json文件。
package.json文件中的main属性,指定模块加载的入口文件,默认是index.js。在index.js文件中,除了模块代码之外,主要使用require命令加载其余模块,使用module.exports变量输出模块接口。
下面是一个例子,将HTML文件中的特殊字符转为HTML实体。
/** * Escape special characters in the given string of html. * * @param {String} html * @return {String} */ module.exports = { escape: function(html) { return String(html) .replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, ''') .replace(/</g, '<') .replace(/>/g, '>'); }, /** * Unescape special characters in the given string of html. * * @param {String} html * @return {String} */ unescape: function(html) { return String(html) .replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, '\'') .replace(/</g, '<') .replace(/>/g, '>'); } };
完成代码之后,再加一个README.md文件,用来给出说明文本。
最后,使用npm publish命令发布。
npm publish