Bootstrap

Malagu 框架开发 React 应用新体验

前言

是基于 TypeScript 的 Serverless First、可扩展和组件化的应用框架。

简介

Malagu React 组件是对 React 框架的集成,将 React 框架通用的功能进行了一定的封装,比如渲染到 Dom 树、路由的定义、上下文扩展等等。

使用方法

Malagu 组件就是一个普通的 npm 包,可以通过以下命令安装:

yarn add @malagu/react 

安装完后,就可以直接运行了。

路由

React 路由定义使用时 react-router 和 react-router-dom 两个模块,目前提供了两种方式定义路由:

@View 定义路由

// 形式一 
@View('/page1')
export class Page1 extends React.Component { ... }  

// 形式二 
@View({ path: '/page1', component: Page1 }) 
export default class {}  

function Page1() { ... } 

传统方式定义路由

@Router()
export class RouterImpl extends React.Component {
    render() {
        return (
            
                
                    ...
                
            
        );
    }
}

前端路由前缀配置

如果你的前端路由存在一个统一的路由前缀,你可以通过配置前端路由前缀来避免在每个路由上书写同样的路由前缀。具体配置如下:

frontend:
  malagu:
    react:
      path: /xxx

History 配置

框架通过如下接口获取路由 History 对象:

export interface HistoryProvider  {
    provide(): History
}

框架已经提供了默认的实现如下:

import { Component, Value } from '@malagu/core';
import { HistoryProvider } from './app-protocol';
import { History, createHashHistory, createBrowserHistory, createMemoryHistory } from 'history';

@Component(HistoryProvider)
export class HistoryProviderImpl implements HistoryProvider {

    protected history: History;

    constructor(
        @Value('malagu.react.history') protected readonly historyOptions: any
    ) {
        const historyCreatorMap: { [key: string]: any } = {
            hash: createHashHistory,
            browser: createBrowserHistory,
            memory: createMemoryHistory
        };
        if (this.historyOptions) {
            const { type, ...options } = historyOptions;
            const create = historyCreatorMap[type] || createHashHistory;
            this.history = create(options);
        } else {
            this.history = createHashHistory();
        }

    }

    provide(): History {
        return this.history;
    }

}

框架默认的 History 类型为 browser,你也可以通过属性覆盖的方式设置其他类型的 History。自定义 History 类型如下:

frontend:
  malagu:
      history:
        type: hash

关联布局

路由定义告诉 React 如何根据浏览的路由渲染 React 的页面组件,但是业务往往存在很多共性的东西,我们在实际的业务项目开发中,我们会这些共性的东西抽取到一个或多个布局组件中去,将页面中差异的部分作为布局组件的子节点渲染。当然布局组件之间也可以相互嵌套。如果你采用传统方式定义路由,此时,布局的写法与传统并没有什么区别。

// 形式一:视图关联一个简单布局组件
@View({ path: '/page1', layout: Layout })
export class Page1 extends React.Component { ... }

function Layout() {
...
}

// 形式二:视图关联一个父视图作为布局组件

@View({ path: '/loyout/page1', layout: Layout })
export class Page1 extends React.Component { ... }

@View({ path: '/loyout/page2', layout: Layout })
export class Page2 extends React.Component { ... }

@View('/loyout')
export class Layout extends React.Component {
...
}

默认布局

当你的视图页面没有设置布局时,视图页面会渲染到一个默认的布局组件,默认布局组件是空的布局组件,什么事情都没有做。在真实的业务中,往往大部分页面都会服用以一个布局组件,你可以通过如下自定义默认默认布局组件:

@DefaultLayout()
export class DefaultLayout extends React.Component<{}, {}> {

    render() {
        return {this.props.children};
    }
}

如果你想让某个视图页面不渲染到默认布局,你可以如下设置:

@View({ path: '/page1', isDefaultLayout: false })
export class Page1 extends React.Component { ... }

对于一些登录注册页面还是挺有用的。

小结

布局组件可以是一个视图页面,也可以是一个普通的组件。对于简单的布局,使用普通组件即可。对于复杂的布局可能会存在布局嵌套布局的情况,可以通过布局视图的方式来实现。

上下文

通过 @View 定义路由,路由组件将托管给框架负责创建,这种情况下,我们如何在路由组件外包裹上下文组件呢?我们可以通过如下方式注入上下文组件:

import * as React from 'react';
import { Context } from '@malagu/react';
import { ThemeProvider as Provider, THEME_REACT_CONTEXT_PRIORITY } from './theme-protocol';
import { ThemeProvider } from '@material-ui/core';
import { Autowired } from '@malagu/core/lib/common/annotation/detached';

@Context()
export class ThemeContext extends React.Component {

    static priority = THEME_REACT_CONTEXT_PRIORITY; // 上下文组件包裹的优先级

    @Autowired(Provider)
    protected readonly provider: Provider;

    render(): React.ReactElement {
        const { children } = this.props;
        return (
            
                {children}
            );
    }
}

其中,静态属性 priority 用来定义上下文组件包裹的优先级,因为我们可以定义多个上下文组件。

开启 CDN

有时候,我们不希望 React 模块与前端业务代码打包到一起,而是希望通过 CDN 的方式加载 React 相关的代码或者样式,我们可以通过开启 CDN 加载实现。开启方式

在 Malagu 框架中,我们可以很容易开启 CDN 加载。方式如下:

  • 在项目 malagu.yml 文件中配置 mode 属性

mode: cdn #
# 有多个 mode 情况下
mode:
  - cdn
  - xxx

  • 通过命令行选项指定 mode

malagu deploy -m cdn
# 有多个 mode 情况下
malagu deploy -m cdn,xxx

实现原理在组件 @malagu/react 中,提供了两个配置文件 malagu.yml 和 malagu-cdn.yml,默认情况下,只会加载 malagu.yml,表示不开启 CDN 加载,当指定 mode 为 cdn 时,按照 Malagu 配置文件加载规则,就会加载 malagu-cdn.yml 配置文件,表示开启了 CDN 加载。malagu-cdn.yml 内容如下:

frontend:
  malagu:
    webpack:
      config:
        externals:
          react: React
          react-dom: ReactDOM
      htmlWebpackTagsPlugin:
        react: https://unpkg.com/react@16/umd/react.production.min.js
        react-dom: https://unpkg.com/react-dom@16/umd/react-dom.production.min.js

如果你想替换默认的 CDN 加载地址,配置如下:

# 在项目的 malagu.yml 文件中配置
frontend:
  malagu:
    webpack:
      htmlWebpackTagsPlugin:
        react: xxxxxxx
        react-dom: xxxxxxx

工具类

PathResolver:根据路由计算完整路由,会基于路由前缀来计算。

属性汇总

malagu.yml 属性文件内容如下:

frontend:
  malagu:
    react:
      path: ${malagu.server.path}
      history:
        type: browser

malagu-cdn.yml 属性文件内容如下:

frontend:
  malagu:
    webpack:
      config:
        externals:
          react: React
          react-dom: ReactDOM
      htmlWebpackTagsPlugin:
        react: https://unpkg.com/react@16/umd/react.production.min.js
        react-dom: https://unpkg.com/react-dom@16/umd/react-dom.production.min.js

相关链接