(翻譯)Mantra官方文檔

Mantra

工做草案-版本0.2.0css

簡介

這是一個Mantra草案規範,一個由Kadira建立的Meteor的應用程序架構。 它幫助開發人員構建可維護的,面向將來的Meteor應用程序。react

版權

The MIT License (MIT)git

Copyright (c) 2016 Kadira Inc.github

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 「Software」), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:服務器

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.react-router

THE SOFTWARE IS PROVIDED 「AS IS」, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.架構

轉載個人翻譯也要署名哦~app

內容

(這兒有個目錄,可我不想寫啦,在個人博客中目錄會自動生成哦~)composer

1 概述

Mantra是Meteor的應用程序架構。 有了Mantra,咱們試圖實現如下兩個主要目標。
1.可維護性
可維護性是與大型團隊合做的關鍵因素。 咱們經過對咱們應用程序的每一個部分進行單元測試來實現這一點,同時遵循一個標準。 這樣很容易讓新手加入團隊。
2.將來證實(Future Proof)
JavaScript就像是一個充滿選擇的土地。 咱們對每一個問題有不止一個最佳解決方案。 如今很難說什麼是最好的解決方案,將來會有什麼變化。
Mantra依賴一套將持續很長時間的核心原則,咱們容許其餘人根據須要進行更改。ide

1.1 Mantra裏有什麼?

  • 它有一個現代的,基於React的UI組件層。

  • 它有一個在你的應用程序中定義業務邏輯的地方, 咱們稱之爲 動做(action)

  • Mantra自己不提供狀態管理,但它容許你使用各類各樣的狀態管理器,包括Meteor / Tracker,Redux,Rx.js Observable,Promise和幾乎任何其餘的狀態管理器。

  • 它有一種經過組合容器(containers)將狀態(states)和動做(action)集成到UI組件中的方法。

  • 它容許你依賴注入。

  • 它幫助你單元測試UI,動做和集成(容器)。

  • 它對目錄佈局,文件命名和其餘內容都有一些標準。

1.2 Mantra不是什麼?

  • 它不是一個應用程序平臺,應用程序平臺專一於應用打包,傳輸,部署等任務。Mantra使用Meteor做爲應用程序平臺。

  • 它不是一個樣板,即便咱們有着一樣的目錄結構。

  • 它不是一個代碼生成器,咱們可能會有一個代碼生成工具(譯者注:已經有了,詳見mantra-cli),但這並非Mantra的核心部分

1.3 Mantra是什麼?

  • 它是一套如何構建Meteor應用程序的標準。

  • 它還帶有一套已經被整理過的且被維護着的庫,幫助你在Meteor上實現Mantra。

1.4 爲何要規範?

Mantra是一個應用程序架構。 會有不少人使用Mantra,包括應用程序開發人員,工具建立者,教程做者和項目經理,因此擁有一個每一個人都遵循的標準是很是重要的。 這就是爲何要規範。

1.5 在你閱讀以前

雖然本規範是以一種簡單易懂的語句編寫的。 可是,若是你對如下領域有充分的瞭解,你讀起來可能會感到流暢。

  • ES2015

  • React

  • React Containers

  • Meteor基礎(Pub / Sub,Tracker,ReactiveDict等)

請參閱附錄A以瞭解有關上述領域的更多信息

2 核心組件

如下是Mantra的核心組件及其組織方式:

2.1 聚焦客戶端

Mantra特別注重你的應用程序的客戶端。 Mantra不會將客戶端和服務端代碼混合在一塊兒;相反,它建議代碼共享。緣由是:

  • 客戶端是你應該付出不少努力的地方。它是你的代碼庫中最大的一部分。服務器端代碼庫相對更容易組織和管理。

  • 客戶端應用程序將使用schema與服務器交互。客戶端應用程序不該該關心服務端如何實現的。
    Mantra不支持Universal Apps這個觀點。它鼓勵不一樣的平臺不一樣的應用程序並在其之間代碼共享。一般有一個服務器與幾個客戶端應用程序之間交互。

基於上述觀點,將客戶端和服務端代碼混合在一塊兒並非一個好主意。
當咱們在本規範中進一步討論Mantra時,它將指的是你應用程序的客戶端。

然而,大多數應用程序將具備服務器端組件。所以,咱們還提供一個服務器端的目錄佈局。有關詳情,請參閱附錄B。

2.2 ES2015語法和ES2015模塊

咱們依賴ES2015及其模塊的不少新特性,所以,爲了使用Mantra,你須要使用Meteor 1.3,它附帶了ES2015模塊。

2.3 使用React做爲UI

咱們使用React做爲Mantra中的UI(表示)層。
UI組件不該該知道關於應用程序的其他部分的任何內容,而且不該該讀取或修改應用程序的狀態。 用於渲染UI組件的數據和事件處理程序應經過props從容器傳遞或做爲動做(action)從其內部的事件處理程序中傳遞。 有時須要在UI組件內使用臨時局部狀態,但該狀態不該在其自身組件外引用。
在編寫UI組件時,你能夠將任意React組件引入程序,包括

  • 在應用程序中定義的其餘UI組件。

  • 來自NPM的UI組件(如material-ui)。

  • 應用中的任何容器(咱們稍後會討論這個問題)。
    還能夠導入任何庫函數並在UI組件中使用它們。 你能夠直接從NPM模塊引入,但不能從任何Meteor軟件包引入。 這些函數應該是純函數

這裏是一個簡單的UI組件:

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;

2.4 動做(Actions)

動做是你在app中寫業務邏輯的地方,它們包括:

  • 驗證

  • State 管理

  • 與遠程數據源的交互
    一個動做是一個簡單的函數,它的第一個參數是整個應用的Context,其餘參數一般來自函數調用時。

注意: 在動做中,你所作的一切都應是基於應用的Context傳遞給動做的其餘參數。你不能引用任何ES2015的modules 除了庫之外。還應避免在動做中使用全局變量。

這是一個動做:

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);
    //這裏調用「config/method_stubs」中的一個method
    //這是咱們如何作延遲補償
    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);
    }

};

2.5 狀態(state)管理

在app中,咱們須要處理不一樣種類的狀態。咱們能夠把他們分爲兩類。

  1. 本地狀態 - 歷來不會同步到遠程服務器的狀態(錯誤,驗證信息,當前的頁面)

  2. 遠程狀態 - 這是一般從遠程服務器獲取並與其同步的state。
    咱們有不一樣的解決方案來管理咱們app中的states,包括:

  • Meteor/MiniMongo (遠程狀態)

  • Tracker/ReactiveDict (本地狀態)

  • FlowRouter (本地狀態)

  • Redux (本地狀態)

  • GraphQL (遠程狀態)

  • Falcor (遠程狀態)
    這就是JavaScript社區一直在創新的地方。所以,Mantra在涉及到狀態管理時是靈活的。你可使用任何你想使用的。

舉個例子,你能夠在你的應用啓動時使用如下狀態:

  • Meteor/MiniMongo (遠程狀態)

  • Tracker/ReactiveDict (本地狀態)

  • FlowRouter (本地狀態)
    以後你也能夠換用其餘解決方案。

注意: Mantra在管理狀態時有一些強制規則

  • 全部的狀態的寫操做都要在其對應的動做中完成。

  • 你既能夠在動做中讀取狀態也能夠在容器中讀取狀態。

  • 不容許直接在UI 組件中讀寫狀態,UI 組件不該該知道任何應用程序中的狀態。

下面的連接是一些關於狀態的使用例子
Reading from local state – inside a container
Writing into local state – inside an action
Reading from a remote state – inside a container

2.6 容器(Containers)

容器是Mantra的集合層,他們執行這些操做:

  • 使用狀態來修改變量,並經過props將它們傳遞到UI組件。

  • 將動做(action)傳遞到UI組件中。

  • 將應用Context中的項目傳遞到UI組件中
    容器是一個React component。

容器使用react‐komposer,它支持不一樣的數據源,包括 Meteor/Tracker, Promises, Rx.js Observable,和幾乎全部其餘的數據源。

一般狀況下,在容器中須要些下面這些函數:

  • composer函數用來從狀態管理中獲取數據。

  • mapper函數從依賴注入層獲取數據。
    建立容器的一些規則:

  • 單個文件中應該只有一個容器,而且這個容器必須是默認引出的(default export)

  • composer函數和mapper函數必須從容器模塊引出(exported)

  • composer函數只能使用props傳遞變量

  • mapper函數必須是純函數(Pure function)

注意:若是須要傳遞應用Context給一個組件,請使用mapper函數函數經過props傳遞。

這是一個容器的例子:

import PostList from '../components/postlist.jsx';
import {useDeps, composeWithTracker, composeAll} from 'mantra-core';

export const composer = ({context}, onData) => {
  const {Meteor, Collections} = context();
  if (Meteor.subscribe('posts.list').ready()) {
    const posts = Collections.Posts.find().fetch();
    onData(null, {posts});
  }
};

export default composeAll(
  composeWithTracker(composer),
  useDeps()
)(PostList);

2.7 應用Context

應用Context可用於全部操做和容器,所以這是應用程序中共享變量的地方,他們包括:

  • Meteor namespace

  • Meteor Collections

  • 本地狀態

  • FlowRouter

  • 其餘Meteor包

  • Redux Stores

  • Rest Clients

  • DDP Clients
    這是一個簡單的應用Context的例子:

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

2.8 依賴注入

Mantra使用依賴注入來隔離應用程序的不一樣部分,包括UI組件和動做(actions)。

咱們使用一個名爲react-simple-di的項目,它在後臺使用React Context。 它同時接受應用Context和動做(action)做爲依賴。

一旦配置,應用程序Context會注入到每個action中,它是action的第一個參數,所以你不須要手動傳遞應用Context。

應用程序Context也能夠在容器中訪問。

2.8.1 配置依賴注入

依賴關係將注入到應用程序的頂級組件中,一般是一個佈局組件,你能夠在你的路由中注入,看一下這個例子:

import React from 'react';
export default function (injectDeps) {
  //這是注入部分:
  const MainLayoutCtx = injectDeps(MainLayout);

  // 路由相關代碼
  //......
}

2.9 路由和組件掛載(Mounting)

注意:當咱們提到組件時,會同時包含容器(containers)和UI組件(UI components)

咱們一般使用路由去掛載組件到UI,這能夠有多種解決方案(好比Flow RouterReact Router)

路由只是一個工具,它在Mantra中的惟一功能就是將組件掛載到UI上。

看一下使用FlowRouter當作路由的例子:

import React from 'react';
import {FlowRouter} from 'meteor/kadira:flow-router';
import {mount} from 'react-mounter';

import MainLayout from '/client/modules/core/components/main_layout.jsx';
import PostList from '/client/modules/core/containers/postlist';

export default function (injectDeps) {
  const MainLayoutCtx = injectDeps(MainLayout);

  FlowRouter.route('/', {
    name: 'posts.list',
    action() {
      mount(MainLayoutCtx, {
        content: () => (<PostList />)
      });
    }
  });
}

注意:若是你須要根據某些條件重定向(例如用戶未被受權),請使用action來代替像FlowRouter的triggersEnter這樣的路由選項。 從組件或容器的composer函數調用action。

2.10 庫

每一個應用程序都有一些實用的功能去完成不一樣的任務,你也能夠經過NPM獲取它們。這些庫將引出(export)函數。因此,你能夠在應用程序的任何位置引用他們,包括動做,組件和容器。

當在組件庫中使用庫函數時,它應該是純函數。

2.11 測試

測試是Mantra的一個核心部分,Mantra幫助你測試應用程序的每個部分。咱們強制執行這些規則有助於你編寫測試程序。你可使用熟悉的工具,如MochaChaiSinon來進行測試。

使用Mantra,你能夠在應用程序中單元測試如下三個核心部分:

2.11.1 UI測試

咱們使用enzyme爲UI進行測試,點擊這裏看一些簡單的測試用例。

2.12 Mantra 模塊

Mantra遵循模塊化架構。 除了「應用程序Context」以外,Mantra的全部組件都應駐留在模塊中。

你能夠建立不少不少的模塊,並經過imports使它們創建聯繫。

2.12.1 應用程序Context和模塊的關係

應用程序Context是應用程序的核心。 它須要在不屬於任何模塊的地方定義。 全部模塊能夠做爲依賴關係訪問應用程序Context,模塊不該更新應用程序Context。

2.12.2 模塊的定義

Mantra模塊能夠包含定義它的主文件文件。 它暴露動做,路由和接受context的函數。 一般是 index.js 文件。

一個簡單的模塊定義像這樣:

export default {
  // 可選的
  load(context, actions) {
    // 作一些模塊的初始化工做
  },
  // 可選的
  actions: {
    myNamespace: {
      doSomething: (context, arg1) => {}
    }
  },
  // 可選的
  routes(injectDeps) {
    const InjectedComp = injectDeps(MyComp);
    // 加載路由並將"被注入的組件"顯示在屏幕上。
  }
};
2.12.3 隱式模塊

若是一個模塊沒有任何action和路由,或者不須要任何初始化,那麼能夠避免使用定義文件。這種隱式模塊可能包含如下內容:

  • UI組件

  • 容器

2.12.4 模塊容器和UI組件的關係

模塊容器和UI組件應可以經過ES2015模塊引入(imported)。

2.12.5 模塊動做

模塊能夠經過命名空間暴露動做。 這些命名空間對於應用程序是全局的,模塊應該保證它們是惟一的。 一個模塊能夠暴露多個命名空間。

最後,來自每一個模塊的全部這些命名空間都被合併,而且能夠在action和容器內訪問。

2.12.6 路由

你可使用任意的路由庫進行路由。若是須要,也能夠在多個模塊中使用路由定義。

2.12.7 核心模塊

Mantra是100%模塊化的,應用程序中應至少有一個模塊, 咱們稱之爲核心模塊。 它只是一個簡單的模塊,但你須要在任何其餘模塊以前加載它。 這個模塊最好放在下面這些地方:

  • 核心路由

  • 應用配置

  • 通用庫

  • 通用動做
    和其餘特定的應用代碼中。

根據應用程序,有多種方式組織模塊,詳見附錄C

2.12.8 子模塊

在一個模塊中,不能 包含子模塊。這是爲防止沒必要要的複雜性而作出的決定。 不然,編寫多層嵌套模塊是會很難管理。

2.13 單一入口

使用Mantra,咱們但願咱們的應用是可與預測的。所以,你的應用只能有一個入口點,它就是client/main.js

它將初始化應用程序Context並加載應用程序中的全部模塊。 這裏有一個client / main.js文件示例:

import {createApp} from 'mantra-core';
import {initContext} from './configs/context';

// 模塊
import coreModule from './modules/core';
import commentsModule from './modules/comments';

// 初始化 context
const context = initContext();

// 建立 app
const app = createApp(context);
app.loadModule(coreModule);
app.loadModule(commentsModule);
app.init();

3 目錄佈局

在Mantra中,咱們強制使用一個公共目錄結構。 這是任何應用程序的可維護性的核心部分。

在本節中,咱們只討論客戶端目錄佈局。 要了解如何組織服務器端目錄佈局,請參閱附錄B。

3.1 頂層目錄結構

全部與Mantra相關的代碼都保留在應用程序的client目錄中。 在內部,一般有兩個目錄和一個JavaScript文件。 他們是:

* configs
* modules
* main.js

讓咱們詳細分析下他們中的每個。

3.1.1 configs

此目錄包含應用程序中的根級別配置。 一般,這是一個放置全部模塊通用的應用程序範圍的全局配置的地方。
此目錄中的全部JavaScript文件都應使用默認引出函數(default export function),該功能可啓動某些任務,並在須要時返回。

注意:咱們一般將應用Context放置在這裏的context.js中。

3.1.2 modules

此目錄包含應用程序中的一個或多個模塊(在本身的目錄中)。 這裏至少應該有一個模塊,它一般被稱爲core
在modules目錄中,一般有這些內容:

* actions
* components
* configs
* containers
* libs
* routes.jsx
* index.js

讓咱們學習更多關於這個目錄下的目錄和文件。

3.1.2.1 actions

此目錄包含模塊中的全部action。 這裏有一個目錄佈局的示例:

* posts.js
* index.js
* tests
    - posts.js

posts.js是一個ES2015模塊,它引出具備action的JavaScript對象。 例如,下面是一個簡單的action模塊:

export default {
  create({Meteor, LocalState, FlowRouter}, title, content) {
    //...
  },

  clearErrors({LocalState}) {
    //...
  }
};

而後,在index.js中,咱們引入全部action模塊並聚合全部action。 咱們給每一個模塊一個命名空間。

import posts from './posts';

export default {
  posts
};

在上面的例子中,咱們給posts.js這個action模塊一個命名空間posts

注意:這些命名空間在應用程序中應該是惟一的。 這是模塊的責任。

在tests目錄中,咱們使用action的名稱爲每一個action模塊編寫測試。 請參閱附錄D以瞭解有關測試文件命名約定的更多信息。

點擊這裏查看action的目錄佈局

3.1.2.2 components

Components包含模塊化UI組件。 它的目錄佈局以下:

* main_layout.jsx
* post.jsx
* style.css
* tests
  - main_layout.js
  - post.js
  • 在這個目錄下全部的.jsx文件都必須默認引出。他們必須是一個React類。

  • 你能夠編寫與這些React組件相關的CSS文件,Meteor將爲你綁定它們。
    就像在actions中,這裏也有一個tests目錄。請參閱附錄D以瞭解有關測試文件命名約定的更多信息。

點擊這裏查看components的目錄佈局

3.1.2.3 containers

此目錄包含一組.js文件,每一個文件表示一個容器。 每一個文件應該做爲React Container類默認引出。

這是一個一般的目錄佈局:

* post.js
* postlist.js
* tests
    - post.js
    - postlist.js

這個目錄下也有一個tests目錄。請參閱附錄D以瞭解有關測試文件命名約定的更多信息。
點擊這裏查看containers的目錄佈局

3.1.2.4 configs

這個目錄包含應用中模塊層次的配置。

此目錄中的全部JavaScript文件必須引出一個默認函數,該函數可啓動任何任務,並在須要時返回一些內容。 該函數能夠接受「應用程序Context」做爲第一個參數。

這是一個簡單的config文件:

export default function (context) {
  // do something
}

這些設置能夠在加載這個模塊時被引用和調用。

注意:一般,這是咱們保存用於樂觀更新的Meteor方法存根的地方。

點擊這裏查看configs的目錄佈局

3.1.2.5 libs

此目錄包含一組有效引出函數的JavaScript文件(.js.jsx)。 這也被稱爲庫。 您能夠在tests的子目錄中編寫庫的測試。

3.1.2.6 routes.jsx

這是包含模塊的路由定義的文件。 它有一個默認引出,它是一個函數。 下面是一個典型的路由定義:

import React from 'react';
import {FlowRouter} from 'meteor/kadira:flow-router';
import {mount} from 'react-mounter';

import MainLayout from '/client/modules/core/components/main_layout.jsx';
import PostList from '/client/modules/core/containers/postlist';

export default function (injectDeps) {
  const MainLayoutCtx = injectDeps(MainLayout);

  FlowRouter.route('/', {
    name: 'posts.list',
    action() {
      mount(MainLayoutCtx, {
        content: () => (<PostList />)
      });
    }
  });
}

如上所示,在加載模塊時,將使用一個名爲injectDeps的函數來調用此默認引出,injectDeps函數能夠將依賴項注入到React組件(或容器)中。

3.1.2.7 index.js

這是模塊的定義文件(主文件)。若是不須要執行如下任務則不須要此文件:

  • 加載路由

  • 定義action

  • 在加載模塊時運行配置
    這是一個典型的模塊定義文件:

import methodStubs from './configs/method_stubs';
import actions from './actions';
import routes from './routes.jsx';

export default {
  routes,
  actions,
  load(context) {
    methodStubs(context);
  }
};

在模塊定義中,當模塊加載時,會調用.load()方法,因此,這裏是調用配置的地方。

3.1.3main.js

這是Mantra應用程序的入口點。 它初始化應用程序Context並加載模塊。 所以,它使用一個名爲mantra-core的實用程序庫。

這是一個簡單的main.js文件的例子:

import {createApp} from 'mantra-core';
import initContext from './configs/context';

// 模塊
import coreModule from './modules/core';
import commentsModule from './modules/comments';

// 初始化 context
const context = initContext();

// 建立應用(app)
const app = createApp(context);
app.loadModule(coreModule);
app.loadModule(commentsModule);
app.init();

4 將來的工做

Mantra是一個草稿,將有缺失的部分和咱們能夠作的改進。 咱們已經肯定如下功能對Mantra很重要,他們將在不久的未來可用。
(懶得翻了 過過再更~)

4.1 Server-Side Rendering (SSR)

It’s extremely possible to do SSR with Mantra. We are trying to do this in a tool‐agnostic manner, but the reference implementation will be based on FlowRouter SSR.

4.2 Distributing Mantra Modules via NPM

We could distribute Mantra modules via NPM. Once we do that, we could do reuse a lot of code between apps and organizations.

4.3 Standard for Styling

It’s better to have a standard for styling UI components.

4.4 Standard for Tests

It’s better to have a standard for writing test cases.

4.5 Reusing Composers

Sometimes, we can use reuse composers for the same function in many places. We need to find a pattern for doing that.

相關文章
相關標籤/搜索