这是一篇关于Mantra的Meteor教程。Mantra是一种基于 Meteor 1.3+、React和 ES2015的 Meteor应用架构,主要作用让 Meteor应用代码架构标准化,特别是前端部分,当然它对后端代码的组织也有要求。注意 Mantra 不是一个框架,而是一套如何构建Meteor App 的说明,同时也有配套的开源库来提高代码编写效率。
如果你熟悉 React,Mantra 类似于 Flux,讲究的是对数据流的控制,但是规定得更加细致。
Mantra 的目的是写出更易于理解和维护的代码。它对几乎所有的情况都有一个标准,另外还为Meteor App 增加单元测试覆盖率。
和 Perl类似,JavaScript的一个难点就是同样一个问题有太多实现方式,而且可能都是最佳解决方案。所以经常是不同的人使用不同的方法。Mantra 让Meteor App 有一个统一的结构,遵循相同的标准,就像设计模式一样,降低大家理解代码结构的难度,确保模块之间解耦,像Flux 一样让数据单向流动,这样维护代码更加容易。
Mantra 使用的原则很有前瞻性,能够很长时间不会过时,同时也允许其他人做必要的改变。
现在的 Web App 的大部分代码都是在前端。后端的代码逻辑相对简单也好管理,后端的难点在于性能优化,特别是大并发的处理,数据库等。
Mantra 的核心在如何组织客户端代码。它倡导前后端代码分离,前端不用知道后端代码是如何实现的,但是可以代码共享。因为是基于React 又侧重前端,所以Mantra 很类似React 的那些标准,例如Flux,Redux 等,解决的问题也类似,都是控制数据流data flow,让代码更易理解维护。如果你对React 熟悉,理解Mantra 就不难。如果理解有困难,建议多看看React 的高级用法,例如stateless/pure function,Higher Order Components 等。
Mantra 不相信Universal App,就是不相信一套前端代码适应所有终端平台。它鼓励一套后端代码,但是为每个前端平台开发单独的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可以让大家少写重复的代码,又可以在不同模块之间分享变量。
处理业务逻辑的模块。包括验证,状态管理和远程数据交互。
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);
}
};
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里的无状态纯函数实现,UI只负责展示界面,没有逻辑、状态等处理。
有两种状态:本地状态(客户端)和远程状态(服务器)。本地状态不和外界发生联系;远程状态需要和外界,例如数据库同步数据。
类似 Flux里的 store概念 ,Meteor有不同的方式实现,例如 MiniMongo,ReactiveDict等。Mantra在这方面很灵活,没有要求用哪一种。但是还是有一些规则
. Action 里可以读写 state
. Container 里只能读 state
. UI 组件里既不能读也不能写 state,只能由props 传入
原文来自:简书/荆雷