摘要:我們通過對我們應用程序的每個部分進行單元測試來實現(xiàn)這一點,同時遵循一個標準。它幫助你單元測試,動作和集成容器。當在組件庫中使用庫函數(shù)時,它應該是純函數(shù)。
Mantra
工作草案-版本0.2.0
簡介這是一個Mantra草案規(guī)范,一個由Kadira創(chuàng)建的Meteor的應用程序架構。 它幫助開發(fā)人員構建可維護的,面向未來的Meteor應用程序。
版權The MIT License (MIT)
Copyright (c) 2016 Kadira Inc.
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.
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.
轉載我的翻譯也要署名哦~
內(nèi)容(這兒有個目錄,可我不想寫啦,在我的博客中目錄會自動生成哦~)
1 概述Mantra是Meteor的應用程序架構。 有了Mantra,我們試圖實現(xiàn)以下兩個主要目標。
1.可維護性
可維護性是與大型團隊合作的關鍵因素。 我們通過對我們應用程序的每個部分進行單元測試來實現(xiàn)這一點,同時遵循一個標準。 這樣很容易讓新手加入團隊。
2.未來證明(Future Proof)
JavaScript就像是一個充滿選擇的土地。 我們對每個問題有不止一個最佳解決方案。 現(xiàn)在很難說什么是最好的解決方案,未來會有什么變化。
Mantra依賴一套將持續(xù)很長時間的核心原則,我們允許其他人根據(jù)需要進行更改。
它有一個現(xiàn)代的,基于React的UI組件層。
它有一個在你的應用程序中定義業(yè)務邏輯的地方, 我們稱之為 動作(action)。
Mantra本身不提供狀態(tài)管理,但它允許你使用各種各樣的狀態(tài)管理器,包括Meteor / Tracker,Redux,Rx.js Observable,Promise和幾乎任何其他的狀態(tài)管理器。
它有一種通過組合容器(containers)將狀態(tài)(states)和動作(action)集成到UI組件中的方法。
它允許你依賴注入。
它幫助你單元測試UI,動作和集成(容器)。
它對目錄布局,文件命名和其他內(nèi)容都有一些標準。
1.2 Mantra不是什么?它不是一個應用程序平臺,應用程序平臺專注于應用打包,傳輸,部署等任務。Mantra使用Meteor作為應用程序平臺。
它不是一個樣板,即使我們有著同樣的目錄結構。
它不是一個代碼生成器,我們可能會有一個代碼生成工具(譯者注:已經(jīng)有了,詳見mantra-cli),但這并不是Mantra的核心部分
1.3 Mantra是什么?它是一套如何構建Meteor應用程序的標準。
它還帶有一套已經(jīng)被整理過的且被維護著的庫,幫助你在Meteor上實現(xiàn)Mantra。
1.4 為什么要規(guī)范?Mantra是一個應用程序架構。 會有很多人使用Mantra,包括應用程序開發(fā)人員,工具創(chuàng)建者,教程作者和項目經(jīng)理,所以擁有一個每個人都遵循的標準是非常重要的。 這就是為什么要規(guī)范。
1.5 在你閱讀之前雖然本規(guī)范是以一種簡單易懂的語句編寫的。 但是,如果你對以下領域有充分的了解,你讀起來可能會感到流暢。
ES2015
React
React Containers
Meteor基礎(Pub / Sub,Tracker,ReactiveDict等)
請參閱附錄A以了解有關上述領域的更多信息
2 核心組件以下是Mantra的核心組件及其組織方式:
2.1 聚焦客戶端Mantra特別注重你的應用程序的客戶端。 Mantra不會將客戶端和服務端代碼混合在一起;相反,它建議代碼共享。原因是:
客戶端是你應該付出很多努力的地方。它是你的代碼庫中最大的一部分。服務器端代碼庫相對更容易組織和管理。
客戶端應用程序?qū)⑹褂胹chema與服務器交互??蛻舳藨贸绦虿粦撽P心服務端如何實現(xiàn)的。
Mantra不支持Universal Apps這個觀點。它鼓勵不同的平臺不同的應用程序并在其之間代碼共享。通常有一個服務器與幾個客戶端應用程序之間交互。
基于上述觀點,將客戶端和服務端代碼混合在一起并不是一個好主意。
當我們在本規(guī)范中進一步討論Mantra時,它將指的是你應用程序的客戶端。
然而,大多數(shù)應用程序?qū)⒕哂蟹掌鞫私M件。因此,我們還提供一個服務器端的目錄布局。有關詳情,請參閱附錄B。
2.2 ES2015語法和ES2015模塊我們依賴ES2015及其模塊的很多新特性,因此,為了使用Mantra,你需要使用Meteor 1.3,它附帶了ES2015模塊。
2.3 使用React作為UI我們使用React作為Mantra中的UI(表示)層。
UI組件不應該知道關于應用程序的其余部分的任何內(nèi)容,并且不應該讀取或修改應用程序的狀態(tài)。 用于渲染UI組件的數(shù)據(jù)和事件處理程序應通過props從容器傳遞或作為動作(action)從其內(nèi)部的事件處理程序中傳遞。 有時需要在UI組件內(nèi)使用臨時局部狀態(tài),但該狀態(tài)不應在其自身組件外引用。
在編寫UI組件時,你可以將任意React組件引入程序,包括
在應用程序中定義的其他UI組件。
來自NPM的UI組件(如material-ui)。
應用中的任何容器(我們稍后會討論這個問題)。
還可以導入任何庫函數(shù)并在UI組件中使用它們。 你可以直接從NPM模塊引入,但不能從任何Meteor軟件包引入。 這些函數(shù)應該是純函數(shù)。
這里是一個簡單的UI組件:
import React from "react"; const PostList = ({posts}) => (2.4 動作(Actions)); export default PostList;{posts.map(post => (
- {post.title}
))}
動作是你在app中寫業(yè)務邏輯的地方,它們包括:
驗證
State 管理
與遠程數(shù)據(jù)源的交互
一個動作是一個簡單的函數(shù),它的第一個參數(shù)是整個應用的Context,其他參數(shù)通常來自函數(shù)調(diào)用時。
注意: 在動作中,你所做的一切都應是基于應用的Context傳遞給動作的其他參數(shù)。你不能引用任何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); //這里調(diào)用“config/method_stubs”中的一個method //這是我們?nèi)绾巫鲅舆t補償 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 狀態(tài)(state)管理
在app中,我們需要處理不同種類的狀態(tài)。我們可以把他們分為兩類。
本地狀態(tài) - 從來不會同步到遠程服務器的狀態(tài)(錯誤,驗證信息,當前的頁面)
遠程狀態(tài) - 這是通常從遠程服務器獲取并與其同步的state。
我們有不同的解決方案來管理我們app中的states,包括:
Meteor/MiniMongo (遠程狀態(tài))
Tracker/ReactiveDict (本地狀態(tài))
FlowRouter (本地狀態(tài))
Redux (本地狀態(tài))
GraphQL (遠程狀態(tài))
Falcor (遠程狀態(tài))
這就是JavaScript社區(qū)一直在創(chuàng)新的地方。因此,Mantra在涉及到狀態(tài)管理時是靈活的。你可以使用任何你想使用的。
舉個例子,你可以在你的應用啟動時使用以下狀態(tài):
Meteor/MiniMongo (遠程狀態(tài))
Tracker/ReactiveDict (本地狀態(tài))
FlowRouter (本地狀態(tài))
之后你也可以換用其他解決方案。
注意: Mantra在管理狀態(tài)時有一些強制規(guī)則
所有的狀態(tài)的寫操作都要在其對應的動作中完成。
你既可以在動作中讀取狀態(tài)也可以在容器中讀取狀態(tài)。
不允許直接在UI 組件中讀寫狀態(tài),UI 組件不應該知道任何應用程序中的狀態(tài)。
下面的鏈接是一些關于狀態(tài)的使用例子
Reading from local state – inside a container
Writing into local state – inside an action
Reading from a remote state – inside a container
容器是Mantra的集合層,他們執(zhí)行這些操作:
使用狀態(tài)來修改變量,并通過props將它們傳遞到UI組件。
將動作(action)傳遞到UI組件中。
將應用Context中的項目傳遞到UI組件中
容器是一個React component。
容器使用react‐komposer,它支持不同的數(shù)據(jù)源,包括 Meteor/Tracker, Promises, Rx.js Observable,和幾乎所有其他的數(shù)據(jù)源。
通常情況下,在容器中需要些下面這些函數(shù):
composer函數(shù)用來從狀態(tài)管理中獲取數(shù)據(jù)。
mapper函數(shù)從依賴注入層獲取數(shù)據(jù)。
創(chuàng)建容器的一些規(guī)則:
單個文件中應該只有一個容器,并且這個容器必須是默認引出的(default export)
composer函數(shù)和mapper函數(shù)必須從容器模塊引出(exported)
composer函數(shù)只能使用props傳遞變量
mapper函數(shù)必須是純函數(shù)(Pure function)
注意:如果需要傳遞應用Context給一個組件,請使用mapper函數(shù)函數(shù)通過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
本地狀態(tài)
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的第一個參數(shù),因此你不需要手動傳遞應用Context。
應用程序Context也可以在容器中訪問。
依賴關系將注入到應用程序的頂級組件中,通常是一個布局組件,你可以在你的路由中注入,看一下這個例子:
import React from "react"; export default function (injectDeps) { //這是注入部分: const MainLayoutCtx = injectDeps(MainLayout); // 路由相關代碼 //...... }2.9 路由和組件掛載(Mounting)
注意:當我們提到組件時,會同時包含容器(containers)和UI組件(UI components)
我們通常使用路由去掛載組件到UI,這可以有多種解決方案(比如Flow Router和React 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: () => () }); } }); }
2.10 庫注意:如果你需要根據(jù)某些條件重定向(例如用戶未被授權),請使用action來代替像FlowRouter的triggersEnter這樣的路由選項。 從組件或容器的composer函數(shù)調(diào)用action。
每個應用程序都有一些實用的功能去完成不同的任務,你也可以通過NPM獲取它們。這些庫將引出(export)函數(shù)。所以,你可以在應用程序的任何位置引用他們,包括動作,組件和容器。
2.11 測試當在組件庫中使用庫函數(shù)時,它應該是純函數(shù)。
測試是Mantra的一個核心部分,Mantra幫助你測試應用程序的每一個部分。我們強制執(zhí)行這些規(guī)則有助于你編寫測試程序。你可以使用熟悉的工具,如Mocha,Chai和Sinon來進行測試。
使用Mantra,你可以在應用程序中單元測試以下三個核心部分:
UI組件 - Example
Actions - Example
Container composers 和 mappers部分 - Example
我們使用enzyme為UI進行測試,點擊這里看一些簡單的測試用例。
2.12 Mantra 模塊Mantra遵循模塊化架構。 除了“應用程序Context”之外,Mantra的所有組件都應駐留在模塊中。
你可以創(chuàng)建很多很多的模塊,并通過imports使它們建立聯(lián)系。
應用程序Context是應用程序的核心。 它需要在不屬于任何模塊的地方定義。 所有模塊可以作為依賴關系訪問應用程序Context,模塊不應更新應用程序Context。
Mantra模塊可以包含定義它的主文件文件。 它暴露動作,路由和接受context的函數(shù)。 通常是 index.js 文件。
一個簡單的模塊定義像這樣:
export default { // 可選的 load(context, actions) { // 做一些模塊的初始化工作 }, // 可選的 actions: { myNamespace: { doSomething: (context, arg1) => {} } }, // 可選的 routes(injectDeps) { const InjectedComp = injectDeps(MyComp); // 加載路由并將"被注入的組件"顯示在屏幕上。 } };
如果一個模塊沒有任何action和路由,或者不需要任何初始化,那么可以避免使用定義文件。這種隱式模塊可能包含以下內(nèi)容:
UI組件
容器
庫
模塊容器和UI組件應能夠通過ES2015模塊引入(imported)。
模塊可以通過命名空間暴露動作。 這些命名空間對于應用程序是全局的,模塊應該保證它們是唯一的。 一個模塊可以暴露多個命名空間。
最后,來自每個模塊的所有這些命名空間都被合并,并且可以在action和容器內(nèi)訪問。
你可以使用任意的路由庫進行路由。如果需要,也可以在多個模塊中使用路由定義。
Mantra是100%模塊化的,應用程序中應至少有一個模塊, 我們稱之為核心模塊。 它只是一個簡單的模塊,但你需要在任何其他模塊之前加載它。 這個模塊最好放在下面這些地方:
核心路由
應用配置
通用庫
通用動作
和其他特定的應用代碼中。
根據(jù)應用程序,有多種方式組織模塊,詳見附錄C
在一個模塊中,不能 包含子模塊。這是為防止不必要的復雜性而做出的決定。 否則,編寫多層嵌套模塊是會很難管理。
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(); // 創(chuàng)建 app const app = createApp(context); app.loadModule(coreModule); app.loadModule(commentsModule); app.init();3 目錄布局
在Mantra中,我們強制使用一個公共目錄結構。 這是任何應用程序的可維護性的核心部分。
3.1 頂層目錄結構在本節(jié)中,我們只討論客戶端目錄布局。 要了解如何組織服務器端目錄布局,請參閱附錄B。
所有與Mantra相關的代碼都保留在應用程序的client目錄中。 在內(nèi)部,通常有兩個目錄和一個JavaScript文件。 他們是:
* configs * modules * main.js
讓我們詳細分析下他們中的每一個。
此目錄包含應用程序中的根級別配置。 通常,這是一個放置所有模塊通用的應用程序范圍的全局配置的地方。
此目錄中的所有JavaScript文件都應使用默認引出函數(shù)(default export function),該功能可啟動某些任務,并在需要時返回。
注意:我們通常將應用Context放置在這里的context.js中。
此目錄包含應用程序中的一個或多個模塊(在自己的目錄中)。 這里至少應該有一個模塊,它通常被稱為core。
在modules目錄中,通常有這些內(nèi)容:
* actions * components * configs * containers * libs * routes.jsx * index.js
讓我們學習更多關于這個目錄下的目錄和文件。
此目錄包含模塊中的所有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的目錄布局
Components包含模塊化UI組件。 它的目錄布局如下:
* main_layout.jsx * post.jsx * style.css * tests - main_layout.js - post.js
在這個目錄下所有的.jsx文件都必須默認引出。他們必須是一個React類。
你可以編寫與這些React組件相關的CSS文件,Meteor將為你綁定它們。
就像在actions中,這里也有一個tests目錄。請參閱附錄D以了解有關測試文件命名約定的更多信息。
點擊這里查看components的目錄布局
此目錄包含一組.js文件,每個文件表示一個容器。 每個文件應該作為React Container類默認引出。
這是一個通常的目錄布局:
* post.js * postlist.js * tests - post.js - postlist.js
這個目錄下也有一個tests目錄。請參閱附錄D以了解有關測試文件命名約定的更多信息。
點擊這里查看containers的目錄布局
這個目錄包含應用中模塊層次的配置。
此目錄中的所有JavaScript文件必須引出一個默認函數(shù),該函數(shù)可啟動任何任務,并在需要時返回一些內(nèi)容。 該函數(shù)可以接受“應用程序Context”作為第一個參數(shù)。
這是一個簡單的config文件:
export default function (context) { // do something }
這些設置可以在加載這個模塊時被引用和調(diào)用。
注意:通常,這是我們保存用于樂觀更新的Meteor方法存根的地方。
點擊這里查看configs的目錄布局
此目錄包含一組有效引出函數(shù)的JavaScript文件(.js或.jsx)。 這也被稱為庫。 您可以在tests的子目錄中編寫庫的測試。
這是包含模塊的路由定義的文件。 它有一個默認引出,它是一個函數(shù)。 下面是一個典型的路由定義:
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: () => () }); } }); }
如上所示,在加載模塊時,將使用一個名為injectDeps的函數(shù)來調(diào)用此默認引出,injectDeps函數(shù)可以將依賴項注入到React組件(或容器)中。
這是模塊的定義文件(主文件)。如果不需要執(zhí)行以下任務則不需要此文件:
加載路由
定義action
在加載模塊時運行配置
這是一個典型的模塊定義文件:
import methodStubs from "./configs/method_stubs"; import actions from "./actions"; import routes from "./routes.jsx"; export default { routes, actions, load(context) { methodStubs(context); } };
在模塊定義中,當模塊加載時,會調(diào)用.load()方法,所以,這里是調(diào)用配置的地方。
這是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(); // 創(chuàng)建應用(app) const app = createApp(context); app.loadModule(coreModule); app.loadModule(commentsModule); app.init();4 未來的工作
Mantra是一個草稿,將有缺失的部分和我們可以做的改進。 我們已經(jīng)確定以下功能對Mantra很重要,他們將在不久的將來可用。
(懶得翻了 過過再更~)
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 NPMWe 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 StylingIt’s better to have a standard for styling UI components.
4.4 Standard for TestsIt’s better to have a standard for writing test cases.
4.5 Reusing ComposersSometimes, we can use reuse composers for the same function in many places. We need to find a pattern for doing that.
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/80985.html
摘要:哈哈,我理解,架構就是骨架,如下圖所示譯年月個有趣的和庫前端掘金我們創(chuàng)辦的使命是讓你及時的了解開發(fā)中最新最酷的趨勢。 翻譯 | 上手 Webpack ? 這篇就夠了! - 掘金譯者:小 boy (滬江前端開發(fā)工程師) 本文原創(chuàng),轉載請注明作者及出處。 原文地址:https://www.smashingmagazine.... JavaSrip... 讀 Zepto 源碼之代碼結構 - ...
摘要:誠然,宣稱官方中文文檔,確實不妥,這只是個人社區(qū)的行為,改正就好了至于合入官方的途徑,只需有翻譯成果,也不難做到最后,一個站點接些貼片廣告,哪有什么不妥我所了解到的社區(qū)翻譯情況,大致如上。 近幾天,很多公眾號發(fā)布了 Python 官方文檔的消息。然而,一個特別奇怪的現(xiàn)象就發(fā)生了,讓人啼笑皆非。 Python 文檔的中文翻譯工作一直是默默無聞,幾個月前,我還吐槽過這件事《再聊聊Pytho...
閱讀 2567·2021-11-22 12:05
閱讀 3453·2021-10-14 09:42
閱讀 1686·2021-07-28 00:15
閱讀 1989·2019-08-30 11:08
閱讀 1487·2019-08-29 17:31
閱讀 932·2019-08-29 16:42
閱讀 2340·2019-08-26 11:55
閱讀 2119·2019-08-26 11:49