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