Javascript 越來越紅,NodeJS 也越來越強大,說真的,在十幾年前怎麼也不會想到 Javascript 這個東西可以在後端執行。但是他就是真的發生了,也許會覺得很神奇,不過,既然有新的東西不斷地冒出來,那麼適時的去接觸也是挺合理的。
是說 JS 寫來寫去都沒有長進的說(抹臉
NodeJS
作者是這位天才 Ryan Dahl,把 Google 的 v8 引擎拿來寫出一套 framework,就叫做 nodejs。至於你說他能幹麼?以他目前快速發展的狀況來看,不知道以後能不能上太空?
語言特性是這樣,由於是從 Javascript 演進而來,所以是相當純粹的事件驅動(event driven)的方式在運作,沒有任何的阻塞(blocking)的問題,寫起來跟一般的 Javascript 沒什麼兩樣,就是不斷地 callback、callback、再 callback...
#缺點是
如果不搭配一些模組的話,要實做一個 HTTP Server,幾乎是要從 HTTP 連線協定開始做起。個人覺得,難度是一回事,去理解這些事情又是另一回事。把他列為缺點其實也是嚴重了點,充其量只是說,要去做這些事情稍顯繁雜而已(被揍飛
第一次
既然說是第一次就上手,那麼上述那些複雜的東西就留給大家自己去研究。我們這邊來講一些輕鬆寫意又自然的東西,首先,你可能必須要知道的事情有,
NPM(Node Package Manager)是一個 NodeJS 的套件管理系統,原本與 NodeJS 是拆開來的,後來在 0.6.x 之後就放在一起了。這個東西相當的方便,你需要什麼套件,用他來安裝、移除、更新就可以了。
至於說 CoffeeScript 嘛,因為他是新的東西所以我放了上來(眾歐
首先是要安裝 nodejs,你可以去他的官網下載壓縮檔,下載下來就可以裝了,以下都是以 Ubuntu/node-0.6.14 為例子,
wget -c http://nodejs.org/dist/v0.6.14/node-v0.6.14.tar.gz
tar zxf node-v0.6.14.tar.gz
cd node-v0.6.14
./configure
make
sudo make install
如果不能安裝,你可能需要安裝下列套件,
git-core curl build-essential openssl libssl-dev
當然,身為 Ubuntu 的使用者,加個 PPA 用 apt 來裝也是很合理的,
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs
裝好之後就可以看一下版本,
$ node -v
v0.6.14
$ npm -v
1.1.12
這樣就裝完了。另外,用 PPA 安裝的 NodeJS,我在檢查 NPM 版本的時候會怪怪的,不知道最近有沒有改掉?
Web Server
我們要用 NodeJS 來作一個簡易的 Web Server,但是我不想自己刻協定怎麼辦?沒關係,我們找一些模組來用,
其中 GeddyJS 是比較特殊的,他是一個蠻標準的 MCV 架構的套件,但是我之所以沒有用他的原因,是他的 View 使用的是 EJS,而我用的是另一套叫做 Jade。所以,把他列出來是想說,應該也有不少使用 EJS 的高手,所以放上來給大家參考一下。
至於 connect 則是因為,他有很多 Middleware 可以使用,如果你覺得你用不到,你不裝也是可以的。由於我在後面會講到一些東西,是要使用他的,所以這邊就先裝起來放了。
怎麼安裝?
$ npm install connect
$ npm install express
裝好之後就可以開使用了,什麼?太簡單?沒辦法啊,就這麼簡單。然後我們可以開始寫第一支 js 了,
var express = require("express"),
app = express.createServer(express.logger());
app.get("/", function(req, res) {
res.sendfile(__dirname + '/index.html', function(err) {
if (err) res.send(404);
});
});
port = process.env.PORT || 3000;
app.listen(port, function() {
console.log("Listening on " + port);
});
然後,我們再準備一支 index.html 來當作我們的輸出,
<h1>Hello NodeJS</h1>
接著就可以把 NodeJS 跑起來了,
$ node web.js
然後用你的瀏覽器打開 http://localhost:3000/ 就看得到結果了。然後或許你就想,哇,好方便,那我就一直寫 app.get
然後不斷地增加 html 檔案,就可以做完靜態網站了耶!
錯!
萬一我要用圖片怎麼辦?我的 Javascript 外部檔案怎麼辦?我的 CSS 檔案怎麼辦?沒關係,哥哥我都幫你想好了,
var express = require("express"),
app = express.createServer(express.logger());
app.get("/", function(req, res) {
res.sendfile(__dirname + '/index.html', function(err) {
if (err) res.send(404);
});
});
app.get(/(.*)\.(jpg|gif|png|ico|css|js|txt)/i, function(req, res) {
res.sendfile(__dirname + "/" + req.params[0] + "." + req.params[1], function(err) {
if (err) res.send(404);
});
});
port = process.env.PORT || 3000;
app.listen(port, function() {
console.log("Listening on " + port);
});
嗯,我們用一點小小地正規,只要把網址過濾一下,並且將合法的檔案直接送往前端,這樣就解決囉!至於你說會不會有 mime-type
的問題?嗯,這些事情他已經幫你解決掉了,除非你是使用相當特殊的文件,否則不會遇到檔案類別問題。
歐,我們的靜態網站做完了,可以收工了(喂
使用樣板
每次都要寫一大堆 .html 檔案,當你的檔案越來越多的時候,改起來就相對麻煩。而且,我們也想把一些變數傳進去用,這個時候樣板引擎就是個解決方法。Express 所支援的樣板引擎相當多元,
這裡有剛剛提到的 EJS 跟 Jade,而其實我現在慢慢地再改用 CoffeeKup,所以,這裡我挑了 Jade 來作一些簡單介紹。喔,為什麼不是介紹 CoffeeKup?因為這樣我還要再講一次 CoffeeScript,所以我還是講 Jade 就好了(飄走
我們在 Express 要怎麼使用他呢?請看底下簡單範例,
var express = require("express"),
app = express.createServer(express.logger());
app.configure(function() {
app.set("views", __dirname + "/views");
app.set("view engine", "jade");
});
app.get("/", function(req, res) {
res.sendfile(__dirname + '/index.html', function(err) {
if (err) res.send(404);
});
});
app.get(/(.*)\.(jpg|gif|png|ico|css|js|txt)/i, function(req, res) {
res.sendfile(__dirname + "/" + req.params[0] + "." + req.params[1], function(err) {
if (err) res.send(404);
});
});
port = process.env.PORT || 3000;
app.listen(port, function() {
console.log("Listening on " + port);
});
我們將 View 的資料夾設定在 views
底下,然後將樣板引擎指定為 jade
,這樣,我們就可以開始寫一個 index.jade 這樣的樣板檔案了。請記得,檔案是放在 views
資料夾下面的,
h1 "Hello NodeJS"
這樣就可以了嗎?當然不是,我們還要修改一下 web.js 才行,
var express = require("express"),
app = express.createServer(express.logger());
app.configure(function() {
app.set("views", __dirname + "/views");
app.set("view engine", "jade");
});
app.get("/", function(req, res) {
res.render("index", {layout: false});
});
app.get(/(.*)\.(jpg|gif|png|ico|css|js|txt)/i, function(req, res) {
res.sendfile(__dirname + "/" + req.params[0] + "." + req.params[1], function(err) {
if (err) res.send(404);
});
});
port = process.env.PORT || 3000;
app.listen(port, function() {
console.log("Listening on " + port);
});
這樣就可以運行了,使用樣板的好處是,你不用一直重新啟動 node 的服務,只要改了樣板內容,他就會做改變。並不會因為他是樣板引擎而需要重新啟動 node 的服務。那,我要怎麼傳變數進去呢?請看,
app.get("/", function(req, res) {
res.render("index", {
layout: false,
nickname: "閃光洽"
});
});
樣板要怎麼接收呢?
h1= nickname
關於 Jade 還有相當多的變化可用,在他的 gitHub 說明中寫得還算詳盡,包含了陣列、邏輯判斷、字串處理等等。想要知道更多詳細操作的人,可以去他們的 gitHub 看看。
WebSocket
因為新的瀏覽器開始實做 websocket 的部份(我沒有說 IE),所以 NodeJS 在一開始就對這一塊有部份的支援,不過因為當初改版頻繁,所以有些東西其實寫完之後,改版就爛掉的事情時常發生,也被人詬病。
BUT!
救星出現了,他叫做 socket.io,你應該很好奇他要做什麼?舉個簡單的例子,聊天室那麼多人一來一往,訊息即時性的發佈與傳遞,就可以靠他來完成。其實 NodeJS 本身就有這種機制,只是當初並不完善,而有這些模組的助力,讓這些事情變簡單而已。
當然是要安裝的,
$ npm install socket.io
然後就請看官方的範例(喂,其實我說正格的,官方的範例相當完整。這個模組其實只是替我們做掉了 NodeJS 關於 Event 這個部份而已,他並沒有很囉唆的做很多事情。
而且他的好處是,我也可以拿去給前端用,就此,聊天室一來一往的傳送就可以串起來了。當然,WebSocket 可以做的事情不只聊天室,只是舉這個例子比較容易理解而已。所以我貼一下官方範例,應該不過份吧 XD
var express = require("express"),
app = express.createServer(express.logger()),
io = require("socket.io").listen(app);
app.configure(function() {
app.set("views", __dirname + "/views");
app.set("view engine", "jade");
});
app.get("/", function(req, res) {
res.render("index", {layout: false});
});
app.get(/(.*)\.(jpg|gif|png|ico|css|js|txt)/i, function(req, res) {
res.sendfile(__dirname + "/" + req.params[0] + "." + req.params[1], function(err) {
if (err) res.send(404);
});
});
io.sockets.on("connection", function (socket) {
socket.emit("front-end", { hello: "world" });
socket.on("my other event", function (data) {
console.log(data);
});
});
port = process.env.PORT || 3000;
app.listen(port, function() {
console.log("Listening on " + port);
});
我們的 index.jade 也要改一下,
!!! 5
html
head
meta(charset="utf-8")
title "Hello Socket.IO"
script(src="/socket.io/socket.io.js")
body
h1= nickname
script
var socket = io.connect();
socket.on("front-end", function (data) {
console.log(data);
socket.emit("my other event", {my: data});
});
這個 index.jade 也改太多了吧,其實我就只是把他寫得比較規矩一點而已,沒有改很多啦。這個範例做了兩件事,
- 主機監聽了一個叫做
my other event
事件。 - 當連入 / 的時候,主機觸發了
front-end
這個事件。 - 用戶端監聽了一個叫做
front-end
事件。 - 當該事件觸發時,會再觸發
my other event
這個事件。
這樣知道他們兩個人之間發生什麼事情了嗎?其實就像是打來打去鬧來鬧去戳來戳去的小情侶而已嘛。
關於資料庫
當然是用目前最熱門的 Mongo 資料庫,其實你要用 MySQL 也不是不行,
不過這裡以 MongoDB 為例子,當然,你的系統需要安裝 MongoDB,才能用囉!
$ npm install mongodb
應用程式的部份稍做修改,
var express = require("express"),
app = express.createServer(express.logger()),
io = require("socket.io").listen(app),
mongodb = require("mongodb"),
mongodbServer = new mongodb.Server("localhost", 27017, {
auto_reconnect: true,
poolSize: 10
}),
db = new mongodb.Db("mydatabase", mongodbServer);
app.configure(function() {
app.set("views", __dirname + "/views");
app.set("view engine", "jade");
});
app.get("/", function(req, res) {
res.render("index", {layout: false});
});
app.get(/(.*)\.(jpg|gif|png|ico|css|js|txt)/i, function(req, res) {
res.sendfile(__dirname + "/" + req.params[0] + "." + req.params[1], function(err) {
if (err) res.send(404);
});
});
io.sockets.on("connection", function (socket) {
socket.emit("front-end", { hello: "world" });
socket.on("my other event", function (data) {
console.log(data);
db.open(function() {
db.collection("test", function(error, collection) {
collection.insert({
data: data
});
db.close();
});
});
});
});
port = process.env.PORT || 3000;
app.listen(port, function() {
console.log("Listening on " + port);
});
以上的例子,是當用戶端觸發了 my other event
的時候,會將用戶傳來的 data
儲存在 mydatabase
這個資料庫中,且儲存的集合是 test
,欄位的名稱叫做 data
。
當然,例子太簡單就不好玩了,這裡面有一個 Bug。
這裡有一篇關於 NodeJS 與 MongoDB 的文章,可以參考:
NodeJS 與 MongoDB 的邂逅
小結
NodeJS 對這個世界的衝擊當然不只如此而已,Javascript 變成顯學這件事情可能也是始料未及。不過,這些事情總是會讓人興奮的,就端看未來會如何去發展了!
結果到最後我真的沒提到 CoffeeScript
有 Bug 才叫做人生。
推廣
2012 JSDC.tw - 台灣第一界 JavaScript 研討會 歡迎大家來(報名好像額滿了說