Meteor.js 基礎(chǔ)與最佳實踐

引言

想知道這個新奇又好玩的東西---Meteor是如何工作的?那就太好了,你來對地方了. 我會向你展示一個Meteor項目的方方面面 并會給你一些最好的提示讓你應用在未來自己的Meteor項目當中.

What is Meteor

Meteor能讓你打造非常動態(tài)的的網(wǎng)頁同時代碼量出乎意料之少. 記住一點,Meteor目前還在一個超級測試的版本(原文發(fā)布時候為 preview 0.3.8, 翻譯時為 preview 0.5.7). 所以, 請不要因為工作的不夠完美而感到意外.
Meteor是基于Node.js, 因此, 你所寫的大部分代碼也將會是JavaScript. 這沒什么好驚奇的. 如果你想要一些好的資源來快遞提高你的JavaScript, 可以看一看 JavaScript Garden.
Meteor存儲數(shù)據(jù)所使用的是MongoDB. 盡管如此, 實際上,Meteor使用的是minimongo作為接口. 它只是擁有相對完整的功能而非標準MongoDB的所有功能. 你并不需要確切地知道MongoDB是如何工作的, 但我還是建議你看一看Meteor 的Collections文檔以便知道你能做些什么.
Meteor目前使用的模板引擎只能是 handlebars. 但在未來支持使用別的模板引擎也是很有可能的.(翻譯該文章時已經(jīng)支持使用其他的如 Jade )
最后, 只要你開發(fā)網(wǎng)站, 了解 HTML 和 CSS 都是必須的.

基礎(chǔ)

一個Meteor項目包含的大多都是JavaScript文件. 如果你放置任意一個 *.js文件在你的項目的任何位置, Meteor會自動幫你加載并運行之. 你在Meteor項目中寫的每一個JavaScript文件都會被部署到服務(wù)端和客戶端(額...也不完全是這樣的,繼續(xù)往下看吧!). 之所以Meteor這么,這么的cool,原因之一就是:當你寫了一個JavaScript函數(shù),在客戶端和服務(wù)端都可以使用之!
更cool的是, 比如,你在項目的任意一個地方放置*.less, 那么這些文件都會自動被編譯并發(fā)送至客戶端以及從而包含進頁面當中.
有時候,可能你希望分離開服務(wù)端和客戶端的代碼. 幸運地是, Meteor有這么一對標識可以幫你:Meteor.is_serverMeteor.is_client
下面的例子在瀏覽器的JavaScript控制臺中會輸出Hi. I’m CLIENT,而在服務(wù)端中則會輸出 Hi. I’m SERVER

// This function is available on both the client and the server.
var greet = function(name) {
    console.log("Hi. I'm " + name);
}

// Everything in here is only run on the server.
if(Meteor.is_server) {
    greet("SERVER");
}

// Everything in here is only run on the client.
if(Meteor.is_client) {
    greet("CLIENT");
}

這非常的簡單. 在客戶端和服務(wù)端共用代碼使你能夠做到最大化的代碼復用,這大大地減少了開發(fā)時間.

項目文件結(jié)構(gòu)

可能, 你無數(shù)次地希望不再服務(wù)端和客戶端之間共享所有的一切. 比如你有一些私密的算法在服務(wù)端執(zhí)行,而你絕不希望Meteor把這些算法發(fā)送至客戶端讓全世界都看得見. Meteor提供了兩個"特殊"的目錄來幫助分離客戶端和服務(wù)端的代碼:[project_root]/client/[project_root]/server/. 在server目錄的JavaScript只會在服務(wù)端執(zhí)行而不會發(fā)送至客戶端, 反之亦然. 這就使得我們無須在各個地方都使用Meteor.is_clientMeteor.is_server.取而代之, 對應的放置你的代碼就OK了.

項目的文件結(jié)構(gòu)非常重要, 尤其在考慮到文件的先后加載順序的時候. 假定有兩個文件, 其中一個依賴于另外一個, 你知道你的JavaScript的加載順序嗎? 其規(guī)則如下:

  1. [project_root]/lib里的文件會最先被加載. 顯而易見的, 庫需要放在這個文件
  2. 文件會根據(jù)目錄的深度來進行加載排序,放在深一層的文件會先加載
  3. 同一級的文件會按照字母順序來排序加載.
  4. main.* 會最后加載, 當有代碼需要在其他所有的庫或腳本之后執(zhí)行這會很有用.

Meteor有一些特殊的目錄來幫助你解決分離 client/server的代碼以及加載的順序:

  • [project_root]/lib/ 該目錄下的代碼會在你的 client/server 代碼執(zhí)行之前加載
  • [project_root]/client/ 這個目錄下的代碼只會在瀏覽器端而不會在服務(wù)端執(zhí)行
  • [project_root]/server/ 這個目錄下的代碼只會在服務(wù)端而不會客戶端執(zhí)行
  • [project_root]/public/ 靜態(tài)文件放在這個目錄下, 并且你可以很輕松地在你的html中指向 image.jpg
  • [project_root]/.meteor/ 一些特殊的,和項目相關(guān)的的信息放在這里, 比如你安裝的某些模塊. 你幾乎可以不用關(guān)心這里邊的東西

響應(Reactivity)

Meteor 通過 響應數(shù)據(jù)源(“reactive” data sources) 和 上下文(context) 幫你省去了當數(shù)據(jù)改變時手動替換頁面的麻煩. 當響應數(shù)據(jù)源被更新后,響應上下文會重新運行(A reactive context is just a function that will get re-run if it contains a reactive data source that gets updated.) 剛開始的時候要把思維轉(zhuǎn)換過來可能有些難, 下面的例子將為你解釋清楚.
下面是一個html頁面和一個叫cool_dudeMeteor模板, 以及一個在客戶端運行的JavaScript函數(shù)---傳一個username的值給模板用以渲染.

<html>
  <head>
  </head>
  <body>
    {{> cool_dude }}
  </body>
</html>
<template name="cool_dude">
  <p class="important">{{ username }} sure is one cool dude!</p>
</template>
// On the client:
Template.cool_dude.username = function() {
    return "Andrew Scala";
};

當頁面被渲染的時候會輸出 “Andrew Scala sure is one cool dude!”

模板即是響應的上下文: 如果它依賴于響應數(shù)據(jù)源, 那么當數(shù)據(jù)源改變時它會重渲染自身. 客戶端的session一種響應數(shù)據(jù)源. 它只會在客戶端存儲一些鍵值對, 且當頁面刷新的時候被擦除.

讓我們把上面的例子模板上下文改為響應數(shù)據(jù)源:

// When the app starts,
// associate the key "username" with the string "Andrew Scala"
Meteor.startup(function() {
    Session.set("username", "Andrew Scala");
});

Template.cool_dude.name = function() {
    return Session.get("username");
};

現(xiàn)在,模板將會在Session里取到username的值. 在響應上下文我們有一個響應數(shù)據(jù)源. 如果存儲在Sessionusername的值更改了, 模板會重新渲染新的值到頁面上. 如給username設(shè)置一個新的值:

Session.set("username", "Bill Murray");

只要執(zhí)行了這行代碼,不管在哪, 頁面都會立馬輸出Bill Murray sure is one cool dude!
在這里, 我會列出其他的響應上下文和數(shù)據(jù)源,你也可以到 Meteor的Reactivity文檔自行閱讀.

發(fā)布/訂閱

Note: 在每一個項目的根目錄下執(zhí)行$ meteor remove autopublish, 否則它會默認發(fā)布你的所有數(shù)據(jù)到客戶端, 這顯然不是個好的做法

服務(wù)端發(fā)布數(shù)據(jù)給客戶端使用, 同時,客戶端向服務(wù)端訂閱已發(fā)布的數(shù)據(jù). 在剛開始, 想要明白服務(wù)端發(fā)布數(shù)據(jù)和客戶端訂閱數(shù)據(jù)的關(guān)系會有些困難. 常規(guī)的經(jīng)驗是這樣的: 客戶端只能訪問當前時間點需要操作的數(shù)據(jù), 除此之外的均不能訪問. 舉個例子, 如果你有一個聊天應用, 客戶端不能夠接收你的網(wǎng)站上每一個頻道的所有信息, 而只能是當前所訪問的頻道的信息. 同樣的也不能夠知道別的頻道的用戶.

下面這是一個關(guān)于 發(fā)布/訂閱 的方面教材, 客戶端可以看到數(shù)據(jù)庫中的每一條信息:

var Messages = new Meteor.Collection("messages");

if(Meteor.is_server) {
    Meteor.publish("messages", function() {
        return Messages.find({});
    });
}

if(Meteor.is_client) {
    Meteor.subscribe("messages");
}

當在客戶端Messages.find({}),可以得到數(shù)據(jù)庫里的每一條信息.
我們可以指定一個參數(shù)使得在訂閱的時候縮小范圍只獲取實際需要的信息(如在"cool_people_channel"頻道中的信息)

var Messages = new Meteor.Collection("messages");

if(Meteor.is_server) {
    Meteor.publish("messages", function(channel_name) {
        return Messages.find({channel: channel_name});
    });
}

if(Meteor.is_client) {
    Session.set("current_channel", "cool_people_channel");

    Meteor.autosubscribe(function() {
        Meteor.subscribe("messages", Session.get("current_channel"));
    });
}

Meteor.autosubscribe是一個響應上下文, 意思是只要有響應數(shù)據(jù)源更新,所有在里頭的東西都會重運行. 我們把當前所在的頻道存儲在Session的"current_channel". 如果這個session值改變, 那么訂閱(subscription)也就更新,
這樣我們就能夠訪問其他的信息了. 如果用戶想要加入“breakfast talk”這個頻道. 我們可以運行Session.set("current_channel", "breakfast_talk"), 這會觸發(fā)autosubscribe, 讓我們可以且僅可訪問"breakfast_talk"頻道下的信息.

或許,你很多次都希望發(fā)布所有的collection到客戶端. 仔細思考客戶端實際需要的是什么. 比起發(fā)送所有的文檔, 只發(fā)送特定領(lǐng)域會來得明智些.

服務(wù)端方法

由于客戶端不允許查看數(shù)據(jù)庫中有什么東西, 你一對會好奇客戶端又是如何存儲信息的. 解決辦法是使用Meteor的服務(wù)端方法(Server Methods). 你在服務(wù)端定義了所有的函數(shù)用以一些危險的做法比如修改和更新數(shù)據(jù), 然后讓客戶端像調(diào)用常規(guī)方法那樣call這些函數(shù)并返回值. 客戶端永遠也不會看到具體的實現(xiàn),也不會自己修改數(shù)據(jù). 這些都是服務(wù)端做的事.
想要添加一個用戶到數(shù)據(jù)庫中, 假定有一個叫create_user 的方法--傳入一個username并讓服務(wù)端插入一條記錄. 它會返回給客戶端一個ObjectID這樣客戶端可以抓取到這個用戶的信息并做任何接下來要做的事.

if(Meteor.is_server) {
    Meteor.methods({
        create_user: function(username) {
            console.log("CREATING USER");
            var USER_id = Users.insert({name: username});
            return user_id;
        },
    });
}

// Remember, the client's browser only ever sees the code below:
if(Meteor.is_client) {
    var username = "Andrew Scala";
    Meteor.call("create_user", username, function(error, user_id) {
        Session.set("user_id", user_id);
    });
}

在這個例子中我的 user_id被設(shè)置到客戶端session中, 當任意模板使用到我的user_id
的時候均立馬可以得到更新.

保護你的數(shù)據(jù)

默認的, 你可以在客戶端打開一個JavaScript 控制臺并運行數(shù)據(jù)庫查詢.這非常的不安全.最糟糕的情況是, 當我們在訪問你這么cool的Meteor應用時可以運行Users.remove({})來擦除你的所有數(shù)據(jù).

Meteor即將會做一些措施來更好的保護你的數(shù)據(jù), 但截至目前為止這是唯一的方法可以做到保護的. 這些代碼被包含在了基于 Meteor的 madewith網(wǎng)站中.這個代碼片段阻止了從客戶端進行insert/update/remove等操作. 把下面這段代碼放在你的服務(wù)端代碼中的任意位置:

// Relies on underscore.js. In your project directory:
// $ meteor add underscore
Meteor.startup(function() {
    var collections = ['collection_name_1', 'collection_name_2'];
    _.each(collections, function(collection) {
        _.each(['insert', 'update', 'remove'], function(method) {
            Meteor.default_server.method_handlers['/' + collection + '/' + method] = function() {};
        });
    });
});

敬請關(guān)注

準備好開發(fā)一個真實的Meteor項目了嗎?一起期待吧, Part 2 就在路上了.屆時將引導你完成一個完整的app!

原文來自Andrew Scala Learn Meteor Fundamentals and Best Practices

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妆艘,一起剝皮案震驚了整個濱河市破加,隨后出現(xiàn)的幾起案子秸滴,更是在濱河造成了極大的恐慌绳泉,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伦意,死亡現(xiàn)場離奇詭異火窒,居然都是意外死亡,警方通過查閱死者的電腦和手機默赂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門沛鸵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缆八,你說我怎么就攤上這事曲掰。” “怎么了奈辰?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵栏妖,是天一觀的道長。 經(jīng)常有香客問我奖恰,道長吊趾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任瑟啃,我火速辦了婚禮论泛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蛹屿。我一直安慰自己屁奏,他們只是感情好,可當我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布错负。 她就那樣靜靜地躺著坟瓢,像睡著了一般勇边。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上折联,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天粒褒,我揣著相機與錄音,去河邊找鬼诚镰。 笑死奕坟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的怕享。 我是一名探鬼主播执赡,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼镰踏,長吁一口氣:“原來是場噩夢啊……” “哼函筋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奠伪,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤跌帐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绊率,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谨敛,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年滤否,在試婚紗的時候發(fā)現(xiàn)自己被綠了脸狸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡藐俺,死狀恐怖炊甲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情欲芹,我是刑警寧澤卿啡,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站菱父,受9級特大地震影響颈娜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜浙宜,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一官辽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粟瞬,春花似錦同仆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳖轰。三九已至,卻和暖如春扶镀,著一層夾襖步出監(jiān)牢的瞬間蕴侣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工臭觉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留昆雀,地道東北人。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓蝠筑,卻偏偏與公主長得像狞膘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子什乙,可洞房花燭夜當晚...
    茶點故事閱讀 45,930評論 2 361

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