這是一篇關(guān)于Mantra的Meteor教程。Mantra是一種基于 Meteor 1.3+、React和 ES2015的 Meteor應用架構(gòu),主要作用讓 Meteor應用代碼架構(gòu)標準化,特別是前端部分,當然它對后端代碼的組織也有要求。注意 Mantra 不是一個框架,而是一套如何構(gòu)建Meteor App 的說明,同時也有配套的開源庫來提高代碼編寫效率。
如果你熟悉 React,Mantra 類似于 Flux,講究的是對數(shù)據(jù)流的控制,但是規(guī)定得更加細致。
Mantra 的目的是寫出更易于理解和維護的代碼。它對幾乎所有的情況都有一個標準,另外還為Meteor App 增加單元測試覆蓋率。
和 Perl類似,JavaScript的一個難點就是同樣一個問題有太多實現(xiàn)方式,而且可能都是最佳解決方案。所以經(jīng)常是不同的人使用不同的方法。Mantra 讓Meteor App 有一個統(tǒng)一的結(jié)構(gòu),遵循相同的標準,就像設(shè)計模式一樣,降低大家理解代碼結(jié)構(gòu)的難度,確保模塊之間解耦,像Flux 一樣讓數(shù)據(jù)單向流動,這樣維護代碼更加容易。
Mantra 使用的原則很有前瞻性,能夠很長時間不會過時,同時也允許其他人做必要的改變。
現(xiàn)在的 Web App 的大部分代碼都是在前端。后端的代碼邏輯相對簡單也好管理,后端的難點在于性能優(yōu)化,特別是大并發(fā)的處理,數(shù)據(jù)庫等。
Mantra 的核心在如何組織客戶端代碼。它倡導前后端代碼分離,前端不用知道后端代碼是如何實現(xiàn)的,但是可以代碼共享。因為是基于React 又側(cè)重前端,所以Mantra 很類似React 的那些標準,例如Flux,Redux 等,解決的問題也類似,都是控制數(shù)據(jù)流data flow,讓代碼更易理解維護。如果你對React 熟悉,理解Mantra 就不難。如果理解有困難,建議多看看React 的高級用法,例如stateless/pure function,Higher Order Components 等。
Mantra 不相信Universal App,就是不相信一套前端代碼適應所有終端平臺。它鼓勵一套后端代碼,但是為每個前端平臺開發(fā)單獨的app 來提高用戶體驗,盡量通過模塊化來共享代碼。
其他 Mantra的基本介紹可以參看這篇中文翻譯 http://www.jianshu.com/p/96d6b8e64c3a
下面我來詳細解釋 Mantra的各個部件。
這里介紹的順序和文檔里的不一樣,主要是先從新的概念介紹。下圖是一個典型的Mantra App 的work flow。
mantra_flow.png
應用上下文 context對所有 action和 container開放讀取,所以這是你分享變量的地方。
import *as Collectionsfrom '/lib/collections';import {Meteor}from 'meteor/meteor';import {FlowRouter}from 'meteor/kadira:flow-router';import {ReactiveDict} from 'meteor/reactive-dict';import {Tracker} from 'meteor/tracker';
export default function () {
return {
Meteor,
FlowRouter,
Collections,
LocalState:new ReactiveDict(),
Tracker
};
}
從上面例子中可以看出,context可以讓大家少寫重復的代碼,又可以在不同模塊之間分享變量。
處理業(yè)務(wù)邏輯的模塊。包括驗證,狀態(tài)管理和遠程數(shù)據(jù)交互。
Action 就是一個簡單的函數(shù)而已,第一個參數(shù)必須是應用的上下文Context。Action 不得使用引入除了參數(shù)以外的任何變量和模塊,甚至全局變量,但是可以使用庫函數(shù)。
export default {
create({Meteor, LocalState, FlowRouter}, title, content) {
if (!title || !content) {
return LocalState.set('SAVING_ERROR','Title & Content are required!');
}
LocalState.set('SAVING_ERROR',null);
const id = Meteor.uuid();
// There is a method stub for this in the config/method_stubs
// That's how we are doing latency compensation
Meteor.call('posts.create', id, title, content, (err) => {
if (err) {
return LocalState.set('SAVING_ERROR', err.message);
}
});
FlowRouter.go(`/post/${id}`);
},
clearErrors({LocalState}) {
return LocalState.set('SAVING_ERROR',null);
}
};
Mantra 只使用React 作為UI 組件。
在 UI組件內(nèi)部不需要知道 App的其他任何內(nèi)容,也不應該讀取和修改應用的 state。UI 使用到的數(shù)據(jù)和事件應該由 props從 container傳入,或者通過事件作為 action props 傳入。如果 UI組件使用到本地 state,那么這個 state不應該被外部的任何組件使用,僅限于組件內(nèi)部使用。
Mantra 文檔里給出的代碼示例:
import React from 'react';
const PostList = ({posts}) => (
<div className='postlist'>
<ul>
{posts.map(post => (
<li key={post._id}>
<a href={`/post/${post._id}`}>{post.title}</a>
</li> ))}
</ul>
</div>
);
export default PostList;
上面的例子代碼就是 React里的無狀態(tài)純函數(shù)實現(xiàn),UI只負責展示界面,沒有邏輯、狀態(tài)等處理。
有兩種狀態(tài):本地狀態(tài)(客戶端)和遠程狀態(tài)(服務(wù)器)。本地狀態(tài)不和外界發(fā)生聯(lián)系;遠程狀態(tài)需要和外界,例如數(shù)據(jù)庫同步數(shù)據(jù)。
類似 Flux里的 store概念 ,Meteor有不同的方式實現(xiàn),例如 MiniMongo,ReactiveDict等。Mantra在這方面很靈活,沒有要求用哪一種。但是還是有一些規(guī)則
. Action 里可以讀寫 state
. Container 里只能讀 state
. UI 組件里既不能讀也不能寫 state,只能由props 傳入
原文來自:簡書/荊雷