Meteor Mantra 介绍 - 前端架构详解

Mantra logo

上一篇 Meteor Mantra 介绍 主要是讲了 Mantra 的特点和主要构成,比较抽象,这一次是说说它的具体文件结构,希望可以帮助大家理解 Mantra 的更多细节,以及如何应用。

还有上一篇漏了一个 Routing & Component Mounting 模块。在 Mantra 里,Router 的作用是把组件组装到 UI,组件可以是 container 容器或者 UI 组件。换句话说,Router 就是把我们写的一个个页面组件组合到一起,然后根据用户在浏览器里输入的路径和参数从服务器端获取数据,并调用显示不同的组件。

有两个 Router 开源库可以选用, flow-routerreact-router 。我用 flow-router。

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

从上面代码可以看到,router 的作用就像在 Unix 里把一个硬盘添加到系统里一样,把一个 PostList 的组件添加 mount 到了应用里。

需要注意的是,如果需要重定向,最好使用 action 而不是 FlowRouter 的 triggersEnter。可以在 component 或者容器的 composer 函数里调用 action。

文件结构

建议对比这个 Mantra 例子 - 一个博客系统,来更好理解 Mantra 的文件结构。

所有 Mantra 相关的代码都在一个名为 client 的文件夹里。在这个 client 文件,又有两个文件夹和一个 JavaScript 文件

  • configs
  • modules
  • main.js

下面来具体分析

configs

这里其实就是 Application Context,通常里面的文件命名为 context.js。它是整个应用的配置,可以被所有模块使用。

Application Context 是应用的核心,但不应该定义在任何模块里。所有其他模块都可以读 Application Context 但是不能写,不能修改。

modules

Mantra 使用的是模块化结构(这里的模块不是 ES2015 的模块,而是指结构上的模块,形式上就是一组 ES2015 exports 构成的一个文件夹,完成一个具体的功能)。除了 Application Context,所有的组件都是模块,然后使用 imports 调用。

这个文件夹至少得有一个名为 core 的文件夹。core module 是最先加载的。通常 core 里放

  • core routes
  • 应用配置
  • 通用 lib
  • 通用 actions
  • 其他整个应用分享的代码

一般 core 里就是核心代码,例如 routes。然后再把其他功能分散到其他的模块。当然其他模块也可以有自己的 routes。

不要使用子模块,因为会加入不必要的复杂度,增加维护难度。

通常一个模块再含有以下文件夹和文件

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

下面是他们的具体作用。

actions

顾名思义,这里就是这个模块所有 actions。一般它有这样的结构

  • posts.js
  • index.js
  • tests
    • posts.js

posts.js 就是一个 action,它会输出(export)一个 JavaScript 对象。这个例子前面有讲到。

index.js 的作用是集成所有 actions,然后统一输出。目的是统一命名,解决 namespace 冲突的问题。

components

UI 组件所在处。文件通常有

  • main_layout.jsx
  • post.jsx
  • style.css
  • tests
    • main_layout.js
    • post.js

jsx 文件就是 React 代码。每个 jsx 文件应该有一个 default export,并且是一个 React 类。

containers

应该都是 .js 文件,每个文件是一个容器,输出一个 default 的 React Container 类。

configs

这里是模块级的配置。每个 js 文件输出一个缺省函数,这个函数的第一个参数通常就是 Application Context。

这里通常是 Meteor 的 method stubs 代码,目的是获得 optimistic updates 特性。如果不清楚这个概念可以看看相关资料。官方文档 https://guide.meteor.com/ui-ux.html#optimistic-ui 。简单来说就是在服务器确认、保存用户产生的信息前在客户端显示这些信息,让用户获得更好的体验。

libs

工具函数等辅助代码。可以是 .js 也可以是 .jsx 文件。

routes.jsx

见本文开头介绍。只是注意那个 injectDeps 参数。它的作用是在加载这个模块时注入依赖。

index.js

模块的定义文件

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

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

从上面的例子代码可以看到,它的作用是加载 routes、定义 actions 和初始化、配置上下文。如果不需要这三个功能,那么就用不着这个 index.js,不用定义文件。

main.js

Mantra app 的唯一入口。它初始化 application context,加载各个模块

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

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

// init context
const context = initContext();

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

注意这里使用到了 mantra-core 这个库来初始化 context 和加载模块。


我请朋友 Javen 在 T社上设计了一件简单的 Meteor Tshirt,有灰白两色,链接请戳 https://www.tshe.com/c/1d2e3710 。85折优惠码 f55f4f28,希望各位 Meteor Hacker 喜欢。