轉: Nodejs概述

目錄

簡介

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

版本管理工具nvm

若是想在同一臺機器,同時運行多個版本的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

REPL環境

在命令行鍵入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提供如下一些全局對象,它們是全部模塊均可以調用的。

  • global:表示Node所在的全局環境,相似於瀏覽器中的window對象。
  • process:指向Node內置的process模塊,容許開發者與當前進程互動。
  • console:指向Node內置的console模塊,提供命令行環境中的標準輸入、標準輸出功能。

全局函數:

  • 定時器函數:共有4個,分別是setTimeout(), clearTimeout(), setInterval(), clearInterval()。
  • require:用於加載模塊。

全局變量:

  • _filename:指向當前運行的腳本文件名。
  • _dirname:指向當前運行的腳本所在的目錄。

除此以外,還有一些對象其實是模塊內部的局部變量,指向的對象根據模塊不一樣而不一樣,可是全部模塊都適用,能夠看做是僞全局變量,主要爲module, module.exports, exports等。

module變量指代當前模塊。module.exports變量表示當前模塊對外輸出的接口,其餘文件加載該模塊,實際上就是讀取module.exports變量。

  • module.id 模塊的識別符,一般是模塊的文件名。
  • module.filename 模塊的文件名。
  • module.loaded 返回一個布爾值,表示模塊是否已經完成加載。
  • module.parent 返回使用該模塊的模塊。
  • module.children 返回一個數組,表示該模塊要用到的其餘模塊。

這裏須要特別指出的是,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_modules/bar
  • ../node_modules/bar
  • ../../node_modules/bar
  • /node_modules/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自帶一系列的核心模塊,下面就是其中的一部分:

  • http:提供HTTP服務器功能。
  • url:解析URL。
  • fs:與文件系統交互。
  • querystring:解析URL的查詢字符串。
  • child_process:新建子進程。
  • util:提供一系列實用小工具。
  • path:處理文件路徑。
  • crypto:提供加密和解密功能,基本上是對OpenSSL的包裝。

這些模塊能夠不用安裝就使用。

除了使用核心模塊,還可使用第三方模塊,以及自定義模塊。

自定義模塊

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模塊

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是數據處理的一種形式,能夠用來取代回調函數。舉例來講,傳統形式的文件處理,必須先將文件所有讀入內存,而後調用回調函數,若是遇到大文件,整個過程將很是耗時。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(); });

http模塊

實例:搭建一個HTTP服務器

使用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)對象,擁有如下屬性。

  • url:發出請求的網址。
  • method:HTTP請求的方法。
  • headers:HTTP請求的全部HTTP頭信息。

處理POST請求

當客戶端採用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方法

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服務器

搭建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模塊

基本用法

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模塊默認支持一些事件。

  • newListener事件:添加新的回調函數時觸發。
  • removeListener事件:移除回調時觸發。
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事件。

EventEmitter對象的其餘方法

(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模塊用來與當前進程互動,能夠經過全局變量process訪問,沒必要使用require命令加載。

屬性

process對象提供一系列屬性,用於返回系統信息。

  • process.pid:當前進程的進程號。
  • process.version:Node的版本,好比v0.10.18。
  • process.platform:當前系統平臺,好比Linux。
  • process.title:默認值爲「node」,能夠自定義該值。
  • process.argv:當前進程的命令行參數數組。
  • process.env:指向當前shell的環境變量,好比process.env.HOME。
  • process.execPath:運行當前進程的可執行文件的絕對路徑。
  • process.stdout:指向標準輸出。
  • process.stdin:指向標準輸入。
  • process.stderr:指向標準錯誤。

下面是主要屬性的介紹。

(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.exit():退出當前進程。
  • process.cwd():返回運行當前腳本的工做目錄的路徑。_
  • process.chdir():改變工做目錄。
  • process.nextTick():將一個回調函數放在下次事件循環的頂部。

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); });

cluster模塊

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

每一個項目的根目錄下面,通常都有一個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這兩項,都指向一個對象。該對象的各個成員,分別由模塊名和對應的版本要求組成。對應的版本能夠加上各類限定,主要有如下幾種:

  • 指定版本:好比1.2.2,遵循「大版本.次要版本.小版本」的格式規定,安裝時只安裝指定版本。
  • 波浪號(tilde)+指定版本:好比~1.2.2,表示安裝1.2.x的最新版本(不低於1.2.2),可是不安裝1.3.x,也就是說安裝時不改變大版本號和次要版本號。
  • 插入號(caret)+指定版本:好比ˆ1.2.2,表示安裝1.x.x的最新版本(不低於1.2.2),可是不安裝2.x.x,也就是說安裝時不改變大版本號。須要注意的是,若是大版本號爲0,則插入號的行爲與波浪號相同,這是由於此時處於開發階段,即便是次要版本號變更,也可能帶來程序的不兼容。
  • latest:安裝最新版本。

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

npm簡介

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:列出當前項目的模塊

npm list命令,默認列出當前目錄安裝的全部模塊。若是使用global參數,就是列出全局安裝的模塊。

npm list

npm -global list

npm search:模塊搜索

向服務器端搜索某個模塊,使用search命令(可以使用正則搜索)。

npm search [搜索詞]

若是不加搜索詞,npm search 默認返回服務器端的全部模塊。

npm run

在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, '&amp;') .replace(/"/g, '&quot;') .replace(/'/g, '&#39;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); }, /**  * Unescape special characters in the given string of html.  *  * @param {String} html  * @return {String}  */ unescape: function(html) { return String(html) .replace(/&amp;/g, '&') .replace(/&quot;/g, '"') .replace(/&#39;/g, '\'') .replace(/&lt;/g, '<') .replace(/&gt;/g, '>'); } };

完成代碼之後,再加一個README.md文件,用來給出說明文本。

最後,使用npm publish命令發佈。

npm publish
相關文章
相關標籤/搜索