Meteor Mantra 介绍 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Meteor Official Site
http://www.meteor.com/
Follow @meteorjs on Twitter
russj
V2EX    Meteor

Meteor Mantra 介绍

  •  
  •   russj 2016-07-09 11:15:33 +08:00 4932 次点击
    这是一个创建于 3426 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Mantra 是一种基于 Meteor 1.3+、 React 和 ES2015 的应用程序架构。它不是一个框架,而是一套如何构建 Meteor App 的标准,同时也有一套相关开源库来提高代码编写效率。

    简单来说, Mantra 是关于如何组织你的 Meteor 应用代码的标准,特别是前端部分 (基于 React),当然它对后端代码的组织也有要求。

    如果你熟悉 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 的各个部件。


    这里介绍的顺序和文档里的不一样,主要是先从新的概念介绍,不然对已经熟悉的也难理解。

    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

    处理业务逻辑的模块。包括验证,状态管理和远程数据交互。

    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 里的无状态纯函数实现, UI 只负责展示界面,没有逻辑、状态等处理。

    State 管理

    有两种状态:本地状态(客户端)和远程状态(服务器)。本地状态不和外界发生联系;远程状态需要和外界,例如数据库同步数据。

    类似 Flux 里的 store 概念 (可参考 使用 Meteor 和 React 开发 Web App ), Meteor 有不同的方式实现,例如 MiniMongo , ReactiveDict 等。 Mantra 在这方面很灵活,没有要求用哪一种。但是还是有一些规则

    • Action 里可以读写 state
    • Container 里只能读 state
    • UI 组件里既不能读也不能写 state ,只能由 props 传入

    Dependency Injection 依赖注入

    首先,什么是依赖? Mantra 有两种依赖

    1. context - 通常就是配置, models 和各种数据
    2. actions - 业务逻辑。每个 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 里随意使用了。

    如何使用依赖?

    首先创建一个 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 来监听数据的变化,子组件 UI Component 负责界面渲染和互动。 Controller 就是高阶组件 (Higher Order Components) HOC 来包裹 UI 组件。高阶组件负责数据查询,子组件负责渲染等。

    Mantra 使用 react komposer 来作为 container 获取数据状态。

    container 的规则

    • 每个 jsx 文件只能有一个 container ,而且这个 container 应该是默认 export
    • composer 和 mapper 函数应该从 container 模块输出
    • composer 函数只能使用从 props 输入的值
    • mapper 应该是纯函数

    Note: 基于 Mantra Draft 0.2.0

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4235 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 00:12 PVG 08:12 LAX 16:12 JFK 19:12
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86