Node不是萬能藥!但的確能解決一些關鍵問題。
學習Node不是一件輕鬆事兒,但你所收到的回報是對得起你的付出的。由於當下Web應用開發中的諸多難題惟有JavaScript才能解決。 目錄
1,專家們的警告! 前端
2,Node:幾個小例子
3,Node不是JavaScript,Node能夠運行JavaScript node
4,和Node服務器的交互 程序員
5,快速入門手冊 web
6,解釋器之惑npm
7,基於事件的Web應用 編程
8,Node的用武之地
「你夠酷嗎?來用我吧!」 Node.js 爲最新潮的編程語言提供了一系列很酷的API和工具箱,它能夠直接應用於傳統的Rails、Ajax、Hadoop、甚至能夠某種程度上用於iPhone開發和HTML5。若是你參加過一些大型技術會議,你老是會聽到一些關於Node.js的主題演講,儘管這些話題對普通的開發者來講依然有些難以企及。 你可能已經據說Node.js(有時咱們將其簡稱爲「Node」)是一個服務器端的解決方案,它能夠運行JavaScript,並能夠做爲Web服務來處理HTTP請求。若是這些東東還不至於讓你暈頭轉向的話,轉眼間關於端口、sockets和線程的討論就又成了當下最熱門的話題,你會以爲這些東西讓你眼花繚亂。這些內容真的屬於JavaScript的範疇嗎?爲何世界上那麼多人寧願將JavaScript脫離瀏覽器而運行,更不用說將JavaScript運行於服務器端了? 好消息是,你所聽到的(所想到的)關於Node的一切都是正確的。Node的的確確是屬於網絡編程的範疇,用以處理服務器端的請求和響應。壞消息是和以前的Rails、Ajax和Hadoop同樣,真正實用的技術資料實在太少。等到基於Node的「優秀的」框架成熟以後,技術資料必定會跟得上的,但何須要等到技術書籍和教程都出來以後再去嘗試使用Node呢?如今就使用Node,說不定會給你的代碼帶來意想不到的改觀,甚至讓你的程序變得更易實現。json
專家門的警告!
和大多數技術同樣,Node也是新瓶裝舊酒:它看起來不透明並且很怪異,但獨受小開發團隊的青睞。若是你沒有接觸過Node,則須要學習一些很容易上手的服務器端腳本。你須要花時間來搞清楚Node,由於即使是運行於服務器端的JavaScript,它和客戶端JavaScript也極爲不一樣。實際狀況是,你不得不本身給本身洗腦,以便從新學習理解圍繞JavaScript的事件處理機制、異步IO和一些網絡基礎知識。
不幸的是,這意味着若是你已經用Node做開發超過兩年時間的話,你會以爲這篇文章內容很單調乏並且過於簡單。你會開始尋找新的「刺激」,好比將Node運行於客戶端,或者開始嘗試事件I/O、反射器模式和npm。你會發現Node的世界是如此有趣,甚至不少Node高級技術具備某種史詩般的美感,而這些東西對於初學者來講依然是難於企及的。所以,或許你應該將你掌握的知識分享給你的同伴,尤爲是對於那些不瞭解Node的同窗,當他們開始對Node感興趣時,給他們分享傳授Node高級技術。後端
Node:幾個小例子
首先,你應當意識到Node是用於運行獨立的JavaScript程序的,而不是運行於瀏覽器中的某個HTML片斷裏。它是存放在文件系統中的真實存在的文件,由Node程序執行,以一種守護進程的模式運行,同時打開對某些端口的監聽。
跳過 hello world
最經典的例子固然是「Hello World「,在Node官網(http://nodejs.org/docs/latest )上有源碼。幾乎每一個人都是從Hello World開始接觸Node的。如今讓咱們跳過這個最簡單的例子,來看一些更有趣的例子:實現一個能夠從服務器發送文件到客戶端的程序(而不只僅是發送一段文本到客戶端)。
var sys = require("sys"), http = require("http"), url = require("url"), path = require("path"), fs = require("fs");
http.createServer(function(request, response) { var uri = url.parse(request.url).pathname; var filename = path.join(process.cwd(), uri); path.exists(filename, function(exists) { if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"}); response.end("404 Not Found\n"); return; }
fs.readFile(filename, "binary", function(err, file) { if(err) {
response.writeHead(500, {"Content-Type": "text/plain"}); response.end(err +"\n"); return; }
response.writeHead(200); response.end(file, "binary"); }); });
}).listen(8080);
console.log("Server running at http://localhost:8080/"); 數組
Node不是JavaScript,Node能夠運行JavaScript
剛剛你將NodeFileServer.js存成了某個文件,別擔憂,咱們等下會回過頭來運行它的。如今,讓咱們移步到現實當中來,在Unix中執行典型的配置和編譯命令:
./configure make
make install
這讓咱們確信一個事實:Node不是JavaScript,Node是一個能夠運行JavaScript的程序,但Node絕對不是JavaScript。實際上,Node是基於C寫的程序。能夠經過ls來查看Node/src目錄中的文件,能夠看到Node的源碼:
大多數人會覺得,JavaScript是一門糟糕的語言,更不用說用它來實現服務器端的功能了,其實你只對了一半。不錯,對於操做系統級別的Socket和網絡編程來講,JavaScript可能並不能勝任。但Node並非JavaScript實 現的,它是基於C實現的。C語言是能夠完美的勝任任意量級的網絡編程的。而JavaScript則徹底有能力將指令傳遞給C程序,而後由C程序來操控操做系統「地下城」。實際上,和C語言相比,JavaScript更容易被開發者們接觸到,這是值得引發注意的地方,若是你想用Node進行一些嚴肅的編程的話,這個緣由會被一再說起。 Node的基本用法進一步反映出了Node是如何和JavaScript一塊兒工做的,Node不是JavaScript。你能夠經過命令行來運行它:
— (bdm0509@Bretts-MacBook-Pro Sun,29 May 11)
— — — — — — — — — — (/Users/bdm0509/tmp/Node/src) — — (09:09 $)-> export PATH=$HOME/local/Node/bin:$PATH — (bdm0509@Bretts-MacBook-Pro Sun,29 May 11)
— — — — — — — — — — (/Users/bdm0509/tmp/Node/src) — — (09:09 $)-> cd ~/examples
— (bdm0509@Bretts-MacBook-Pro Sun,29 May 11)
— — — — — — — — — — — — (/Users/bdm0509/examples) — — (09:09 $)-> Node NodeFileServer.js Server running at http://127.0.0.1:1337/
這裏也不會對此作過多介紹,只要知道Node能夠運行JavaScript,這就足夠了。並且你只需學習JavaScript這一門編程語言便可,不用擔憂本身不懂C語言。記住這是最最重要的一點,沒必要了解C也可寫出Node可運行的程序。瀏覽器
和Node服務器的交互
剛纔咱們在Node上運行了NodeFileServer.js。這時你能夠訪問你本機的1337端口,能夠看到正常的輸出。
請求的服務(這是一個提示,同時打開四個五個甚至十個瀏覽器訪問服務器),這也是很容易作到的。Node讓人着迷的地方在於,你徹底能夠用很簡單並且很不起眼的JavaScript程序來完成你想要的這些結果。
快速入門手冊
圍繞Node的話題老是會比純粹運行在服務器端的代碼更值得花點時間來討論。無論怎樣,咱們仍是從一段代碼開始咱們的話題,概覽一下NodeFileServer.js文件,觀察代碼:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');
首先調用了函數require(),require()是程序員最經常使用的函數之一。實際上,在CommonJS規範中也有提到這個函數,在討論到關於JavaScript模塊概念的時候有說起,此外,Davd Flanagan在2009年的一個很酷的實現中也有提到。換句話說,require()對於你來講多是個新鮮事物,但它不是Node隨意添加的一個函數,他是使用JavaScript進行模塊化編程的核心概念,Node將這一特性發揮的淋漓盡致。
接下來,http變量用以建立一個服務器。這個服務使用一個回調函數來處理當產生一個鏈接時的動做。這裏的回調函數並未對請求做過多修飾,僅僅以text/plain格式輸出一個字符串「Hello World」做爲請求響應。這個邏輯很是簡單。
實際上,這裏展現了使用Node的標準模式:
定義交互類型,並得到一個用以處理這個交互的變量(經過require())。 建立一個新的服務(經過createServer())。
給服務綁定一個回調,用以處理請求。包括處理請求的函數應當包括一個請求…,以及一個響應 通知服務器啓動服務,這裏須要指定IP和端口(經過listen)。
解釋器之惑
儘管經過這種方法可使用JavaScript輕易的實現一個服務(無論運行代碼的虛機實際上跑的是C程序仍是其餘什麼程序),這種作法迴避了一個問題:你須要使用JavaScript寫出一個服務器嗎?爲了找到這個問題的答案,咱們來考慮一個很是典型的場景。 JSON的處理
這是一種很是典型的web應用,前臺使用HTML和CSS,JavaScript用來做數據驗證,並和後臺進行數據交互。因爲你處於web交互的最頂端,你使用Ajax提交數據到後臺並從後臺獲取數據,而不是單單依靠表單提交來實現。若是你是這樣作的話,那麼你一樣會很是喜歡使用JSON的。JSON是現在最流行的傳輸數據的格式。 所以,這個Ajax也能夠比做「把在線拍賣網站裏的某些吉他的信息發給我」。這個請求經過網絡到達一個運行PHP程序的服務器。PHP服務器不得不給JavaScript返回不少信息,並且這些信息必須以某種形式的數據包發給客戶端,並且這個數據包是能夠被JavaScript解析的。所以數據能夠打包成數組,而後轉換爲JSON,就像這樣:
$itemGuitar = array( 'id'=>'itemGuitar',
'description'=>'Pete Townshend once played this guitar while his own axe ' . was in the shop having bits of drumkit removed from it.', 'price' => 5695.99,
'urls' => array('http://www.thewho.com', 'http://en.wikipedia.com/wiki/Pete_Townshend') );
$output = json_encode($itemGuitar); print($output);
回到客戶端,JavaScript獲得這個返回的數據包,因爲通過轉換,數據編程了JSON格式。就像這樣:
{
"id": "itemGuitar",
"description": "Pete Townshend once played this guitar...", "price": 5695.99,
"urls": ["http://www.thewho.com", "http://en.wikipedia.com/wiki/Pete_Townshend"] }
這種轉換是標準的,轉換先後也是相互等價的。接下來,就能夠將這個字符串轉換爲JavaScript對象,能夠調用eval(),就像這樣:
var itemDetails = eval('('+ jsonDataString +')');
計算結果是一個普通的JavaScript對象,這個對象的屬性和JSON數組的數據結構保持一致。固然,因爲jsonDataString一般是由服務器返回的,一般須要這樣來解析返回結果:
var itemDetails = eval('('+ request.responseText +')');
這就是最最典型的JSON處理,但存在一個很是嚴重的問題。 對實體代碼微妙的破壞性
(譯註:這個小標題着實讓人費解,做者這裏拐彎抹角的解釋了Node的一個好處,就是前端和後端都採用一樣的語言JavaScript,在做JSON解析時是無障礙的,而當前端使用JavaScript做JSON編碼,後臺用PHP做JSON解碼時,多少會由於多種語言的JSON解析的實現不一樣而帶來一些兼容性問題)
首先,這類代碼的一個主要問題是,它對解釋器的依賴比較嚴重。在上個例子中,解釋器就是指內置的JSON解析器或者實現解析JSON的代碼,這實際上依賴了兩樣東西:和eval()解析響應文本的操做同樣的基於Java的JSON解析器,以及基於PHP的JSON解析器。在PHP5.2.0中已經包含了JSON解析器,但倒是之外部依賴的形式給出的,並非內置於PHP的內核中。
但這並非大肆宣揚解釋器的種種。畢竟解釋器自己還存在不少問題,好比將「I」解析成了「i」,數組中的元素1解釋成了2。固然,在JSON工具正式發佈以前會有大量的測試,以保證在各類複雜場景中都不會出現錯誤,包括在客戶端的解析結果和在服務器端的解析結果徹底一致。不管如何,這都須要大量的測試才行。 無論怎樣,JSON依然存在不少實際的問題。
基於某種語言(基於JavaScript或者PHP)的JSON解析器選擇是一個很大的問題。換句話說,問題不是在於「翻譯」(translation)而在於「翻譯器」(translator)(譯註:做者的意思是說JSON自己的規則沒有問題,反卻是各類語言的JSON實現的質量良莠不齊,甚至有不少bug)。當一個語言的版本比較穩定時,基於這門語言的JSON解析器的運用和推廣會比較快。結果是,JSON解析器變的愈來愈強大,以致於能夠解析任意複雜的數據結構,即使這麼複雜的數據結構根本不會實際用到。反之,每次迭代中(每次計算迭代的路徑和數據類型的組合),也頗有可能出現JSON解釋器沒法解析的數據結構(或者很深的JSON路徑)的狀況
JavaScript中eval()的潛在隱患
正如咱們不用將Node看成一門新的語言來對待同樣,在Node中經過eval()來執行一段代碼也和JavaScript中的eval()同樣(不被推薦)。衆所周知eval()是很是危險的。eval()用以執行一段文本表示的代碼邏輯,能夠理解爲在文本框中「直接敲入SQL代碼來執行查詢」,這是不安全的,這其實是惡意SQL注入。當每次eval()執行一段字符串的時候,(美國)中西部的一隻小狗都會瑟瑟發抖,東部海灘上的某位母親的腳趾會被刺傷並受到詛咒。
eval()很是危險。網上有不少關於此的資料,這裏再也不贅述。能夠用google查詢「eval JavaScript evil」或者「eval JavaScript injection」獲取更多信息。 固然,若是沒有任何其餘上下文的約束,在Node中也是容許使用eval()的,所以eval()的隱患在Node依然存在。畢竟Node的目的並非徹底解決eval()的問題。Node被稱之爲基於事件的JavaScript或基於事件的I/O,這裏所說的「基於事件」是Node中很是重要的概念。但要完全理解什麼是基於事件,以及爲何基於事件能讓你規避eval()的危險,則須要理解JSON在應用之中是如何工做的,此外還要搞清楚適應於web應用典型架構的特有數據結構。