引言
想知道這個新奇又好玩的東西---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_server
和Meteor.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_client
和 Meteor.is_server
.取而代之, 對應的放置你的代碼就OK了.
項目的文件結(jié)構(gòu)非常重要, 尤其在考慮到文件的先后加載順序的時候. 假定有兩個文件, 其中一個依賴于另外一個, 你知道你的JavaScript的加載順序嗎? 其規(guī)則如下:
-
[project_root]/lib
里的文件會最先被加載. 顯而易見的, 庫需要放在這個文件 - 文件會根據(jù)目錄的深度來進行加載排序,放在深一層的文件會先加載
- 同一級的文件會按照字母順序來排序加載.
-
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_dude
Meteor模板, 以及一個在客戶端運行的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ù)源. 如果存儲在Session
的username
的值更改了, 模板會重新渲染新的值到頁面上. 如給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