Introduction to Realtime Web with Meteor.js and Node.js

最近,有許多關(guān)于 Derby.js的激動人心的討論涌現(xiàn)在了我的Twitter Timeline. 我從未使用過能夠幫你做這么多--實時同步客戶端和服務端 的框架. 從本質(zhì)上講, 這使得我們可以自己編寫一個代碼量很少的應用可以讓兩個人編寫同一個 text field–live. 而 Derby幫你處理了在 models 和 views 之間所有的同步. 就如 Google Docs 的協(xié)作編輯那樣.

這非常的偉大, 但經(jīng)過深入的研究, 發(fā)現(xiàn) Derby.js 并沒有我想象中的那么成熟--目前還沒有到1.0版本. 當然, Node.js 和 Meteor.js 也同樣沒有. 但相比起來, 似乎Derby缺少的更多一些. 比如, 據(jù)我目前知道的, 還沒有一個好使的方法來處理sessions. 或許是因為缺乏文檔的原因吧, 但是, 據(jù)說Derby的團隊目前正在開發(fā)authentication. 如果有誰有一些關(guān)于Derby.js 處理sessions的新手指引, 我會很樂意去研究的.

另外一個我經(jīng)常見到被拿來與Derby.js做比較的框架是Meteor.js. 與Derby相似的是,它也能在多個客戶端下實時更新views, 盡管做法上可能跟Derby有點不同. Derby可以較容易的使用多種數(shù)據(jù)庫, 而Meteor則只親近于MongoDB. 事實上, 通過如Mongoose客戶端接入數(shù)據(jù)庫的API與你在服務端所期望的已經(jīng)非常接近了.

比起現(xiàn)在node的一些缺點和爭議, Meteor看起來是非常有趣的選擇用來建立有實時需求的應用. 個人覺得還是Derby基于傳統(tǒng)回調(diào)的編程形式更吸引我, 但在Derby的強大背后,卻缺乏健壯的文檔和一個大的開發(fā)者社區(qū), 這無疑是個很大的打擊. 或許這會隨著時間推移而有所改變吧, 但比起Meteor來說還是會慢很多, 因為后者最近獲得了1100萬美元的資金. 這筆財政資金確保了Meteor的存在以及得到持續(xù)的支持. 對于那些需要財政與發(fā)展穩(wěn)定的框架的開發(fā)者而言, 這筆資金只會讓Meteor更加優(yōu)勝.

今天,讓我們一起來看看如何新建一個真實的但又簡單的Meteor應用. 本質(zhì)上說, 這是基于Tom的 Vimeo screencast的一個新手指引. 與Tom的 Vimeo screencast最大的不同是處理事件的方式. 比起復制粘貼一個Meteor示例的代碼, 我會一步一步的通過自己的方式來處理使用Enter鍵來提交一則訊息. 讓我們開始吧.

Creating a Meteor App

Derby和Meteor 他們共有的一個大加分是他們各自的命令行工具. 與Derby使用Node的內(nèi)置的 npm 工具所不同的是, Meteor使用的是它自己的.

在終端(Mac OS X 和 Linux),執(zhí)行如下的命令. (在這之前請確保你已經(jīng)安裝了Node)

$ curl https://install.meteor.com | /bin/sh

接下來的事Meteor會自己搞定了.

要新建一個項目, 先轉(zhuǎn)到你的工作目錄然后運行下邊的代碼. 這會創(chuàng)建一個目錄, 里邊包括有Meteor和一個最基本模板程序.

$ meteor create chat

現(xiàn)在, 你可以轉(zhuǎn)到該目錄并運行下面的代碼讓它跑起來

$ cd chat
$ meteor
Running on: http://localhost:3000/

想要看到這個最基礎(chǔ)的應用程序, 你只需要在任意一款不過時的瀏覽器下打開http://localhost:3000/

只要你想, 你可以使用Meteor內(nèi)置的meteor deploy命令來部署你的應用到Meteor自己的服務器上

$ meteor deploy my-app-name.meteor.com

只要你更新保存了你的代碼, 所有連接上的瀏覽器都會實時更新其頁面.

Developing the Chat App

通過meteor create命令生成的文件夾里, 你可以看到幾個不同的文件. 如果你的系統(tǒng)設(shè)置了顯示隱藏文件, 那還可以看到一個.meteor文件夾. 這個文件夾包括的Meteor本身, 以及MongoDB數(shù)據(jù)庫的文件.

在你的項目的根目錄, 你可以看到的還有chat.html chat.js chat.css. 這三個文件自解釋?的. HTML包含了這個應用的模板和視圖, 并都通過chat.css來添加樣式. 而這個JavaScript文件則包含了在客戶端和服務端執(zhí)行的代碼. 這非常的重要--請不要把任何東西比如配置數(shù)據(jù)和密碼都放在這里, 因為任何人都能通過查看你的應用程序的源文件看到.

用你最喜歡的文本編輯器打開chat.js. 我個人使用的是Sublime Text 2, 因為它很簡潔并且有多光標功能.
你可以在chat.js看到下面這些代碼

if (Meteor.is_client) {
  Template.hello.greeting = function () {
    return "Welcome to chat.";
  };

  Template.hello.events = {
    'click input' : function () {
      // template data, if any, is available in 'this'
      if (typeof console !== 'undefined')
        console.log("You pressed the button");
    }
  };
}

if (Meteor.is_server) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}

分別注意到if語句里的Meteor.is_clientMeteor.is_server部分. 在這里邊的代碼分別只在客戶端和服務端下執(zhí)行. 這就是Meteor共用代碼的能力了.

刪除掉Meteor.is_server這段代碼并把if (Meteor.is_client)里邊的代碼刪除了,只留如下部分:

if (Meteor.is_client) {

}

注意到, 當你保存了你的腳本之后, 瀏覽器會馬上重新加載這個新腳本.

Creating the View

在我們正式對這個腳本文件動工之前, 我們需要先新建一個視圖用來展示聊天記錄.
在編輯器里打開chat.html并刪除body標簽里邊的代碼. 包括名為hello的template標簽.只留如下部分

<head>
  <title>chat</title>
</head>

<body>

</body>

接著在body標簽里添加下面這句

{% raw %}{{> entryfield}}{% endraw %}

Meteor使用的模板系統(tǒng)與Mustache很相似.大括號{% raw %}{{}}{% endraw %}表示要呈現(xiàn)的內(nèi)容. 通過簡單地在兩對大括號里添加內(nèi)容如{% raw %}{{hello}}{% endraw %}, 模板系統(tǒng)會用hello這個變量的值來替換它. 后面會更詳細的介紹.

注意到了在entryfield這個詞前面有個大于號>了嗎? 使用該符號來指定渲染哪一個模板.

接下來使用下面這段代碼來新建一個名叫entryfield的模板

<template name="entryfield">
    <input type="text" id="name" placeholder="Name" /> <input type="text" id="message" placeholder="Your Message" />
</template>

在這個例子中,template標簽只有一個屬性--用來表示這個template的名字. 也就是當我們渲染的時候需要用來指定的名字.

查看瀏覽器, 你會發(fā)現(xiàn)頁面已經(jīng)刷新了, 輸入框已經(jīng)呈現(xiàn)出來了.

接下來, 在body里邊添加另外的一個mutache標簽用以渲染訊息列表

{% raw %}{{> message}}{% endraw %}

最后, 我們還需要新建一個名叫messages的模板. 在entryfield模板下面添加下面這段代碼

<template name="messages">
    <p>
        {% raw %}{{#each messages}}
            <strong>{{name}}</strong>- {{message}}
       {{/each}}{% endraw %}
    </p>
</template>

注意到each子句. 在Meteor中你可以使用如下的語法來遍歷一個數(shù)組模板

{% raw %}
{{#each [name of array]}}
{{/each}}
{% endraw %}

使用each循環(huán)時,上下文會有所改變. 當引用變量的時候, 實際上你引用的是每一個數(shù)組元素的值.
例如,在我們的chat應用中, 我們遍歷了數(shù)組模板"messages"里邊的每個元素, 該數(shù)組可以像下面這樣,

[
    {
        "name": "Andrew",
        "message": "Hello world!"
    },
    {
        "name": "Bob",
        "message": "Hey, Andrew!"
    }
]

然后, 在each循環(huán)中, 你可以看到{% raw %}{{message}}{% endraw %} {% raw %}{{name}}{% endraw %}, 這會引用 每一個數(shù)組元素的值來替代(Andrew 和 Bob 替換 name, 以及各自的問候信息.)

當返回到你的瀏覽器, 你還看不到任何的改變. 因為訊息數(shù)組還沒被傳送到模板, 所以Meteor遍歷不到任何東西來呈現(xiàn).

你的chat.html最后應該是這樣的

<head>
  <title>chat</title>
</head>
{% raw %}
<body>
  {{> entryfield}}
  {{> messages}}
</body>
{% endraw %}
<template name="entryfield">
    <input type="text" id="name" placeholder="Name" /> <input type="text" id="message" placeholder="Your Message" />
</template>

<template name="messages">
    {% raw %}
    <p>
        {{#each messages}}
            <strong>{{name}}</strong>- {{message}}<br/>
        {{/each}}
    </p>
</template>
{% endraw %}

The Javascript

從現(xiàn)在開始, 我們處理的大部分代碼都是客戶端代碼, 所以, 除非特別說明, 以下的代碼都是在if (Meteor.is_client)代碼塊中.

在我們編寫展示訊息的代碼之前,讓我們先新建一個Collection. 從本質(zhì)上講, 這是一組Models. 換句話說, 在這個chat應用的環(huán)境下, Messages collection保存著整個聊天記錄, 而每條訊息記錄是一個Model.

在if語句前, 添加如下代碼來初始化Collection:

Messages = new Meteor.Collection('messages');

因為我們希望這個Collection可以在客戶端和服務端被創(chuàng)建, 所以我們把它寫在了客戶端代碼塊之外.

由于Meteor為我們做了大部分的工作, 要展示聊天記錄是非常容易的. 只需要把下面的代碼添加進if語句里邊.

Template.messages.messages = function(){
    return Messages.find({}, { sort: { time: -1 }});
}

讓我們拆開來分析這段代碼:
Template.messages.messages = function(){ … }
第一部分Template表示我們正在修改一個模板的行為.
Template.messages.messages = function(){ … }
第二部分messages是模板的名字, 表示是在修改哪一個模板. 例如,如果我們想要對"entryfield"模板做些什么, 只需把代碼改成Template.entryfields.variable = function(){ … }(在這里, 請別這么做)
Template.messages.messages = function(){ … }
第三部分的這個messages代表的是一個這個模板里的一個變量. 還記得我們的each循環(huán)遍歷messages嗎? 這就是那個mesaages.

當你打開瀏覽器時, 頁面還是沒有什么改變. 這是意料之中的事, 因為我們只抓取的訊息, 而沒有展示出來.

此時,你的chat.js應該是這樣的. 是否很驚訝就只需在服務器寫這么些代碼我們就能展示一個實時的聊天記錄應用.

Messages = new Meteor.Collection('messages');

if (Meteor.is_client) {
  Template.messages.messages = function(){
    return Messages.find({}, { sort: { time: -1 }});
  }
}

Adding a Message through the Console

這部分的內(nèi)容是可選的, 當然它有助于你調(diào)試程序. 你可以直接跳過往下學習建立form來響應key press.

如果你想要測試你的訊息顯示代碼, 你可以手動插入一條記錄到數(shù)據(jù)庫. 打開你的瀏覽器控制臺, 并輸入如下:

Messages.insert({ name: 'Andrew', message: 'Hello world!', time: 0 })

這將會在數(shù)據(jù)庫中新建一條記錄, 如果正確的操作了的話,那瀏覽器就會即刻更新這條訊息在頁面上.

The Message Entry Form

返回到chat.js, 我們即將要完成的是允許用戶在輸入框中提交聊天訊息到數(shù)據(jù)庫.
在if語句里頭最下面添加下面這段代碼

Template.entryfield.events = {
  "keydown #message": function(event){
    if(event.which == 13){
      // Submit the form
      var name = document.getElementById('name');
      var message = document.getElementById('message');

      if(name.value != '' && message.value != ''){
        Messages.insert({
          name: name.value,
          message: message.value,
          time: Date.now()
        });

        name.value = '';
        message.value = '';
      }
    }
  }
}

這段代碼比較多, 讓我們一步步分析. 你應該還有印象, 緊跟Template之后的詞表示我們即將要修改的template的名字. 跟前面修改的模板是messages不一樣的是, 這里我們要修改的是entryfield.
template的event屬性會包含有一個對象, 這個對象的keys的格式如下:

"[eventname] [selector]"

比如, 如果我們想綁定了一個click事件給了ID為hello的按鈕. 則需要像下面這樣添加一個events對象

"click #hello": function(event){ … }

在我們這個例子中,我們綁定了一個keydown事件給了ID為message的輸入框. 這個輸入框在我們一開始的chat.html已經(jīng)建好.

events對象里, 每一個key都有一個函數(shù)作為它的值. 當事件被觸發(fā)的時候, 這個event對象會被作為第一個參數(shù)傳給這個函數(shù).在我們的chat應用, 每一次在輸入框中按下一個按鍵(keydown), 這個函數(shù)都會被調(diào)用.

在函數(shù)里的代碼非常簡單. 首先, 我們先要檢測按下的是否"enter"鍵("enter"的keycode 是13). 接著,通過ID來得到兩個輸入框中的DOM元素. 最后, 我們檢查確保輸入框中的值不為空, 用戶不允許發(fā)送空的名字和訊息.

下面的代碼需要注意了, 它會被直接插入到數(shù)據(jù)庫中

Messages.insert({
  name: name.value,
  message: message.value,
  time: Date.now()
});

你會發(fā)現(xiàn),這其實跟我們直接在瀏覽器控制臺中輸入的代碼很相似, 只是我們用DOM元素的值來替換固定值. 另外, 歐文木訥添加了時間值來確保是按照時間來排序.

最后,我們把兩個輸入框設(shè)置為空''

現(xiàn)在, 你可以打開瀏覽器嘗試在輸入框輸入名字和訊息. 按下"enter"后,輸入框會被被清空, 而且這則訊息會立馬出現(xiàn)在你的輸入框下邊. 使用另外一個瀏覽器窗口或者其他瀏覽器打開同樣的鏈接(http://127.0.0.1:3000). 嘗試輸入一則新的訊息, 此時你就會看到Meteor的強大之處.無須單獨寫一行代碼來更新訊息記錄, 就可以實時同步在不同的客戶端瀏覽器中.

Conclusion

While Meteor is pretty cool to work with and there are some pretty useful applications for it, like Derby.js, it is immature. For examples of this, just browse through the documentation and look for the red quotations. For example, the documentation states the following about MongoDB collections:

Currently the client is given full write access to the collection. They can execute arbitrary Mongo update commands. Once we build authentication, you will be able to limit the client’s direct access to insert, update, and remove. We are also considering validators and other ORM-like functionality.

對于任何一個產(chǎn)品而言, 用戶擁有數(shù)據(jù)庫的所有讀寫權(quán)限都是非常大的問題, 非常危險.

原文來自Andrew Munsell Introduction to Realtime Web with Meteor.js and Node.js

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市猬错,隨后出現(xiàn)的幾起案子锦亦,更是在濱河造成了極大的恐慌晾浴,老刑警劉巖痰腮,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機泣刹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來犀被,“玉大人椅您,你說我怎么就攤上這事」鸭” “怎么了掀泳?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵,是天一觀的道長西轩。 經(jīng)常有香客問我员舵,道長,這世上最難降的妖魔是什么藕畔? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任马僻,我火速辦了婚禮,結(jié)果婚禮上注服,老公的妹妹穿的比我還像新娘韭邓。我一直安慰自己,他們只是感情好祠汇,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布仍秤。 她就那樣靜靜地躺著,像睡著了一般可很。 火紅的嫁衣襯著肌膚如雪诗力。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天,我揣著相機與錄音苇本,去河邊找鬼袜茧。 笑死,一個胖子當著我的面吹牛瓣窄,可吹牛的內(nèi)容都是我干的笛厦。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼俺夕,長吁一口氣:“原來是場噩夢啊……” “哼裳凸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起劝贸,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤姨谷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后映九,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梦湘,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年件甥,在試婚紗的時候發(fā)現(xiàn)自己被綠了捌议。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡引有,死狀恐怖瓣颅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情轿曙,我是刑警寧澤弄捕,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布僻孝,位于F島的核電站导帝,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏穿铆。R本人自食惡果不足惜您单,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荞雏。 院中可真熱鬧虐秦,春花似錦、人聲如沸凤优。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筑辨。三九已至俺驶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棍辕,已是汗流浹背暮现。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工还绘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人栖袋。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓拍顷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親塘幅。 傳聞我的和親對象是個殘疾皇子昔案,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容