Meteor Mantra 介紹 (一)- 基本概念

Meteor Mantra 系列文章:

Meteor Mantra 介紹(一)- 基本概念
Meteor Mantra 介紹(二)- 前端架構詳解
Meteor Mantra 介紹(三)- 后端架構解釋
Meteor Mantra 介紹(四)- 博客例子前端代碼解讀
Meteor Mantra 介紹(五)- 博客例子后端代碼解讀
Meteor Mantra 介紹(六)- 使用 mantra-cli 命令行生成源碼


Mantra 是一種基于 Meteor 1.3+份名、React 和 ES2015 的 Meteor 應用架構薄货,主要作用讓 Meteor 應用代碼架構標準化辰狡,特別是前端部分慌申,當然它對后端代碼的組織也有要求呻疹。注意 Mantra 不是一個框架蝌数,而是一套如何構建 Meteor App 的說明打毛,同時也有配套的開源庫來提高代碼編寫效率物遇。

如果你熟悉 React,Mantra 類似于 Flux娘赴,講究的是對數據流的控制规哲,但是規(guī)定得更加細致。

目的

Mantra 的目的是寫出更易于理解和維護的代碼诽表。它對幾乎所有的情況都有一個標準唉锌,另外還為 Meteor App 增加單元測試覆蓋率隅肥。

和 Perl 類似,JavaScript 的一個難點就是同樣一個問題有太多實現方式袄简,而且可能都是最佳解決方案腥放。所以經常是不同的人使用不同的方法。Mantra 讓 Meteor App 有一個統一的結構绿语,遵循相同的標準秃症,就像設計模式一樣,降低大家理解代碼結構的難度吕粹,確保模塊之間解耦种柑,像 Flux 一樣讓數據單向流動,這樣維護代碼更加容易昂芜。

Mantra 使用的原則很有前瞻性莹规,能夠很長時間不會過時,同時也允許其他人做必要的改變泌神。

偏重前端

現在的 Web App 的大部分代碼都是在前端良漱。后端的代碼邏輯相對簡單也好管理,后端的難點在于性能優(yōu)化欢际,特別是大并發(fā)的處理母市,數據庫等。

Mantra 的核心在如何組織客戶端代碼损趋。它倡導前后端代碼分離患久,前端不用知道后端代碼是如何實現的,但是可以代碼共享浑槽。因為是基于 React 又側重前端蒋失,所以 Mantra 很類似 React 的那些標準,例如 Flux桐玻,Redux 等篙挽,解決的問題也類似,都是控制數據流 data flow镊靴,讓代碼更易理解維護铣卡。如果你對 React 熟悉,理解 Mantra 就不難偏竟。如果理解有困難煮落,建議多看看 React 的高級用法,例如 stateless/pure function踊谋,Higher Order Components 等蝉仇。

Mantra 不相信 Universal App,就是不相信一套前端代碼適應所有終端平臺。它鼓勵一套后端代碼量淌,但是為每個前端平臺開發(fā)單獨的 app 來提高用戶體驗吏砂,盡量通過模塊化來共享代碼芯砸。


其他 Mantra 的基本介紹可以參看這篇中文翻譯 http://www.reibang.com/p/96d6b8e64c3a

下面我來詳細解釋 Mantra 的各個部件芬位。


這里介紹的順序和文檔里的不一樣摸袁,主要是先從新的概念介紹撼短。下圖是一個典型的 Mantra App 的 work flow祈秕。

mantra_flow.png

Application Context

應用上下文 context 對所有 action 和 container 開放讀取牺堰,所以這是你分享變量的地方梁呈。

import * as Collections from '/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 可以讓大家少寫重復的代碼摘刑,又可以在不同模塊之間分享變量。

Actions

處理業(yè)務邏輯的模塊刻坊。包括驗證枷恕,狀態(tài)管理和遠程數據交互。

Action 就是一個簡單的函數而已谭胚,第一個參數必須是應用的上下文 Context徐块。Action 不得使用引入除了參數以外的任何變量和模塊,甚至全局變量灾而,但是可以使用庫函數胡控。

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); 
  }
};

UI

Mantra 只使用 React 作為 UI 組件。

在 UI 組件內部不需要知道 App 的其他任何內容旁趟,也不應該讀取和修改應用的 state昼激。UI 使用到的數據和事件應該由 props 從 container 傳入,或者通過事件作為 action props 傳入锡搜。如果 UI 組件使用到本地 state橙困,那么這個 state 不應該被外部的任何組件使用,僅限于組件內部使用耕餐。

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)純函數實現凡傅,UI 只負責展示界面,沒有邏輯蛾方、狀態(tài)等處理像捶。

State 管理

有兩種狀態(tài):本地狀態(tài)(客戶端)和遠程狀態(tài)(服務器)。本地狀態(tài)不和外界發(fā)生聯系桩砰;遠程狀態(tài)需要和外界拓春,例如數據庫同步數據。

類似 Flux 里的 store 概念 (可參考 使用 Meteor 和 React 開發(fā) Web App
)亚隅,Meteor 有不同的方式實現硼莽,例如 MiniMongo,ReactiveDict 等。Mantra 在這方面很靈活懂鸵,沒有要求用哪一種偏螺。但是還是有一些規(guī)則

  • Action 里可以讀寫 state
  • Container 里只能讀 state
  • UI 組件里既不能讀也不能寫 state,只能由 props 傳入

Dependency Injection 依賴注入

首先匆光,什么是依賴套像?Mantra 有兩種依賴

  1. context - 通常就是配置,models 和各種數據
  2. actions - 業(yè)務邏輯终息。每個 action 都以 context 為第一個參數

例如:

const context = { 
  DB, 
  Router, 
  appName: 'My Blog'
};

const actions = { 
  posts: { 
    create({DB, Router}, title, content) { 
      const id = String(Math.random()); 
      DB.createPost(id, title, content); 
      Router.go(`/post/${id}`); 
    } 
  }
};

然后注入依賴夺巩。Mantra 使用 react-simple-di 這個包來進行依賴注入。背后其實就是 React context周崭。這個包接受 Context 和 Actions 作為依賴柳譬。

import {injectDeps} from 'react-simple-di';
import Layout from './layout.jsx';

// 上面定義的 context 和 actions 定義在這里

const LayoutWithDeps = injectDeps(context, actions)(Layout);

現在 LayoutWithDeps 就可以在 app 里隨意使用了。

如何使用依賴续镇?

首先創(chuàng)建一個 UI 組件美澳。可以看到這個組件的依賴是通過 props 傳入的

class CreatePost extends React.Component { 
  render() { 
    const {appName} = this.props; 
    return ( 
      <div> 
        Create a blog post on app: ${appName}. <br/> 
        <button onClick={this.create.bind(this)}>Create Now</button> 
      </div>  
    ); 
  } 

  create() { 
    const {createPost} = this.props; 
    createPost('My Blog Title', 'Some Content'); 
  }
}

使用依賴

const {useDeps} from 'react-simple-di';

// 前面定義的 CreatePost react 組件 

const depsToPropsMapper = (context, actions) => ({ 
  appName: context.appName, 
  createPost: actions.posts.create
});

const CreatePostWithDeps = useDeps(depsToPropsMapper)(CreatePost);

如果你沒有定義自己的 mapper 函數(就是上面的 depsToPropsMapper)摸航, useDeps 將使用下面的默認 mapper 函數制跟,這樣就可以直接使用 context 和 actions 了。

const mapper = (context, actions) => ({ 
  context: () => context, 
  actions: () => actions
});

Mantra 使用依賴注入的目的是隔離代碼忙厌。例如隔離 UI 組件和 actions凫岖。

一旦配置好,Applicaton Context 就會被注入到把 Context 作為第一參數的 action逢净。

Container 同樣也能讀取 Application Context哥放。

不能在子組件里注入依賴,只能是最上層組件爹土,通常就是 Layout Component甥雕,例如下面代碼。

import React from 'react';
export default function (injectDeps) { 
  // See: Injecting Deps 
  const MainLayoutCtx = injectDeps(MainLayout); 
  // Routes related code
}

Container

Container 的作用是集成胀茵、組裝數據社露。它的中文意思是容器,里面包裹的就是 UI 組件琼娘。主要功能:

  • 處理 state峭弟,處理后把值通過 props 傳入 UI 組件
  • 把 action 傳入 UI 組件
  • 把應用 Context 傳入 UI 組件

Container 是一個 React 組件。如這篇文章所述的 Controller-View

Flux

如上圖所示脱拼,使用一個父組件瞒瘸,就是 Mantra 的 container 來監(jiān)聽數據的變化,子組件 UI Component 負責界面渲染和互動熄浓。?Controller 就是高階組件 (Higher Order Components) HOC 來包裹 UI 組件情臭。高階組件負責數據查詢,子組件負責渲染等。

Mantra 使用 react‐komposer 來作為 container 獲取數據狀態(tài)俯在。

container 的規(guī)則

  • 每個 jsx 文件只能有一個 container竟秫,而且這個 container 應該是默認 export
  • composer 和 mapper 函數應該從 container 模塊輸出
  • composer 函數只能使用從 props 輸入的值
  • mapper 應該是純函數

Note: 基于 Mantra Draft 0.2.0

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市跷乐,隨后出現的幾起案子肥败,更是在濱河造成了極大的恐慌,老刑警劉巖劈猿,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拙吉,死亡現場離奇詭異潮孽,居然都是意外死亡揪荣,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門往史,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仗颈,“玉大人,你說我怎么就攤上這事椎例“ぞ觯” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵订歪,是天一觀的道長脖祈。 經常有香客問我,道長刷晋,這世上最難降的妖魔是什么盖高? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮眼虱,結果婚禮上喻奥,老公的妹妹穿的比我還像新娘。我一直安慰自己捏悬,他們只是感情好撞蚕,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著过牙,像睡著了一般甥厦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寇钉,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天刀疙,我揣著相機與錄音,去河邊找鬼摧莽。 笑死庙洼,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播油够,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼蚁袭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了石咬?” 一聲冷哼從身側響起揩悄,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鬼悠,沒想到半個月后删性,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡焕窝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年蹬挺,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片它掂。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡巴帮,死狀恐怖,靈堂內的尸體忽然破棺而出虐秋,到底是詐尸還是另有隱情榕茧,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布客给,位于F島的核電站用押,受9級特大地震影響,放射性物質發(fā)生泄漏靶剑。R本人自食惡果不足惜蜻拨,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抬虽。 院中可真熱鬧官觅,春花似錦、人聲如沸阐污。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笛辟。三九已至功氨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間手幢,已是汗流浹背捷凄。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留围来,地道東北人跺涤。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓匈睁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親桶错。 傳聞我的和親對象是個殘疾皇子航唆,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容