背景


一个应用随着业务的发展往往会变得越来越复杂和臃肿,不同应用之间又往往存在不同维度的共性。组件化能很好的解决上面的问题:


另外,Malagu 框架本身也是一个特别复杂的项目,Malagu 框架同样可以基于自身的组件机制来实现,让 Malagu 框架更具活力。


目标



组件内部结构


组件内部结构图 (2).svg



说明:


组件目录结构


下面是一个前后端一体化组件的代码目录结构,组件也是一个标准 node 模块,在根目录可以放置组件属性配置文件,如下面的 malagu.yml 文件,模式属性配置文件也是放在根目录位置。源代码目录划分成三个目录:前端代码目录(browser)、公共代码目录(common)和后端代码目录(node)。您可以更加具体情况,增删目录,如果组件是一个纯后端组件,browser 和 common 目录可以不需要。组件目录结构中的源代码目录不会强制您按照下面的方式来组织,只不过官方提供的基础组件会按照下面的方式来组织,当然,如果需要开发自己的组件,框架推荐使用与我们相同的组织方式。

.
├── malagu.yml                            # 组件配置【可选】
├── package.json
├── src
│   ├── browser                           # 前端代码目录【可选】
│   │   ├── application-lifecycle.ts
│   │   ├── module.ts
│   │   └── user.view.tsx
│   ├── common                            # 公共代码目录【可选】
│   │   ├── index.ts
│   │   └── user-protocol.ts
│   └── node                              # 后端代码目录【可选】
│       ├── entity
│       │   ├── index.ts
│       │   └── user.ts
│       ├── module.ts
│       └── user-service.ts
└── tsconfig.json


组件形态


组件包含四种种形态:



其中运行时组件,如下图所示:



组件形态.svg


组件之间关系

组件之间关系.svg


说明:


组件属性


配置方式


组件属性通过 yaml 文件来配置,默认在组件项目的根目录下加载 malagu.yml 属性文件,当 malagu.yml 配置了属性 mode: test ,则尝试加载根目录的 malagu-test.yml 属性文件,规则是: malagu-[mode].yml


配置文件分类如下:


模式属性


模式属性,在特定模式下才会生效的属性。模式与环境的关系是多对多的关系,即一个环境对应着一个或多个模式,不同环境之间可以复用同一个的模式。


模式的本质是告诉框架如何加载合并组件的配置文件,以及配置文件的加载顺序(优先加载的属性文件的属性优先级越低)。


有两种方式指定模式


  1. 属性文件中配置 mode
  2. 命令行指定选项 --mode,-m ,支持模式的命令有:
    1. malagu serve
    2. malagu build
    3. malagu deploy


示例一:通过属性文件配置

# 指定单个模式,该模式下,会尝试加载所有组件的 malagu.yml 和 malagu-prod.yml
mode: prod  

#指定多模式,该模式下,会尝试加载所有组件的 malagu.yml、malagu-prod1.yml 和 malagu-prod2.yml
mode: [prod1, prod2]


示例二:通过命令行选项配置


# 指定单个模式,该模式下,会尝试加载所有组件的 malagu.yml 和 malagu-prod.yml
malagu deploy -m prod

#指定多模式,该模式下,会尝试加载所有组件的 malagu.yml、malagu-prod1.yml 和 malagu-prod2
malagu deploy -m prod1,prod2


当即通过属性文件配置,又通过命令行选项配置时,框架将两者配置进行合并,命令行选项配置的模式优先级比属性文件配置高。


模式属性优先级规则



属性划分


在属性文件中可以配置:


其中, frontend 下面的都是前端属性; backend 下面的都是后端属性,其他的为公共属性。


  1. 前端属性示例


frontend:
    malagu:
    foo: bar



  1. 后端属性示例


backend:
    malagu:
    foo: bar


  1. 公共属性示例


malagu:
 foo: bar


还有一种维度的划分:



属性优先级规则


泛化原则


泛化的属性优先级低(以下规则优先级由高到底):


依赖原则


被依赖的组件属性优先级低:


当以上两个原则存在冲突时:


属性规范



属性模板变量


属性值可以使用表达式来引用其他的属性值或者环境变量值。


  1. 引用其他属性值
port: 3000
host: localhost
url: 'https://${host}:${port}'


  1. 引用环境变量值
password: ${env.PASSWORD}


  1. 默认值设置
password: '${env.PASSWORD?:123456}'
  1. 忽略表达式计算


如下属性配置,因为 test 节点存在属性 _ignoreEl 为 true,则 test 节点下的属性或者属性的属性的值不在进行表达式计算。

test:
    a: '${b > 0 ? true: false}'
  _ignoreEl: true 

上面的方案是对某个一个节点进行表达式计算忽略,如何只对单一属性值就行表达式忽略呢?我们可以通过转义符实现。如下:

test:
    a: '\\${b > 0 ? true: false}'


  1. 运行时表达式


上面的写法是在项目编译期间计算表达式的值,如果您需要在应用运行时取运行时的环境变量的话,如下:

password: '${{env.PASSWORD?:123456}}'


表达式更多语法规则请看:Jexl


组件使用


一个组件就是一个 npm 包,组件的使用与 npm 包的使用是一样的。但是,对两个平级的组件而言, package.json 中的依赖顺序会决定组件的加载顺序。


组件加载


Malagu 推荐使用容器来管理对象之间的依赖,也正是把对象放到容器中托管,才让组件具备极强的扩展性,让组件适用更多的场景。对于一个 Malagu 的应用,可能有一个或者多个组件组成,想让应用能够正常跑起来,就必须按照合理的加载顺序,分别加载组件中需要托管到容器的对象。


组件加载机制如下图:


组件加载.svg


从上图可以看出,组件的对象在 module 中声明后,只需要把组件按依赖关系的进行拓扑排序,被依赖的组件优先加载,平级组件按照先后顺序加载,容器按照拓扑排序后的顺序加载 module ,最终构建出整个完成的容器。


组件扩展


扩展方法


自定义一个组件,且依赖需要被扩展的组件,通过属性覆盖、实现扩展接口和直接替换组件内部实现来扩展或改变组件的行为。


扩展策略



扩展能力来源