Core

配置文件

Malagu使用malagu-[mode].yml文件来配置应用程序。

Malagu 框架很多地方使用到了 EL 表达式功能,EL 表达式使用了开源库jexl实现。EL 表达式让组件属性的定义与使用变得极度的灵活。

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

配置文件分类如下:

  • 公共属性文件:malagu.yml
  • 模式属性文件:malagu-[mode].yml

模式属性

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

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

有两种方式指定模式:

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

通过属性文件配置

malagu.yml
# 指定单个模式,该模式下,会尝试加载所有组件的 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

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

模式属性优先级规则

  • malagu.yml < malagu-mode.yml
  • 多模式情况下,写在前面的模式优先级低,例如模式为 prod1,prod2,则 prod2 的属性优先级大于 prod1 的属性
  • 属性文件配置的模式 < 命令行选项配置的模式

属性划分

在属性文件中可以配置:

  • 公共属性
  • 前端属性
  • 后端属性

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

前端属性示例

malagu.yml
frontend:
    malagu:
    foo: bar

后端属性示例

malagu.yml
backend:
    malagu:
    foo: bar

公共属性示例

malagu.yml
malagu:
  foo: bar

还有一种维度的划分:

  • 编译时属性
  • 运行时属性

属性优先级规则

泛化原则

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

  • 前端(后端)属性 > 公共属性
  • 模式属性文件 > 公共属性文件

依赖原则

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

  • 依赖者属性 > 被依赖者属性

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

  • 泛化原则 > 依赖原则

属性规范

  • 前后端代码都需要访问的属性设计成公共属性
  • 公共代码只访问公共属性
  • 属性层级设计:业务名.组件名.属性
  • 属性命名规则:采用小驼峰(推荐)
  • 属性最好提供默认值

在配置文件中使用 EL

在 Malagu 框架中,使用 Yaml 文件配置组件属性。为了让属性普通值与 EL 表达式区别开来,Malagu 使用 ${}${{}}包裹 EL 表达式。一个属性值可以包含多个 EL 表达式和字符串的组合,并且 EL 表达式还可以嵌套使用。

  • ${} 编译时 EL 表达式,在项目编译构建时计算
  • ${{}} 运行时 EL 表达式,在项目运行时计算

引用其他属性值

malagu.yml
port: 3000
host: localhost
url: "https://${host}:${port}"

引用环境变量值

malagu.yml
password: ${env.PASSWORD}

默认值设置

malagu.yml
password: "${env.PASSWORD?:123456}"

忽略表达式计算

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

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

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

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

运行时表达式

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

malagu.yml
password: "${{env.PASSWORD?:123456}}"

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

获取配置文件内容

获取配置文件内容目前有两种方法,一般情况下,使用@Value即第一种方法可以基本满足需求。

@Value

使用 @Value 装饰器可以把属性值注入到某一个类对象中。@Value 支持一个 EL 表达式参数,@Value 就是通过该 EL 表达式参数去引用并且计算属性的值。因为默认就是 EL 表达式,所以不需要使用 ${} 和 $null 包裹 EL 表达式。

import { Component, Value } from "@malagu/core";

@Component()
export class A {
  @Value("foo") // 支持 EL 表达式语法,如 @Value('obj.xxx')、@Value('arr[1]') 等等
  protected foo: string;
}

在 ConfigUtil 中使用

@Value装饰器类似,不需要使用 ${}${{}}包裹 EL 表达式。在 ConfigProvider中使用 EL 表达式与 ConfigUtil是一样的,因为ConfigUtil底层是基于 ConfigProvider 封装实现。

export function Logo(props: NavItemProps) {
  const { label, icon, ...rest } = ConfigUtil.get("malagu.shell.logo");
  props = { ...rest, ...props };
  return <NavItem size="medium" gap="xsmall" label={label} icon={<Icon icon={icon} />} hoverIndicator={false} activatable={false} {...props} />;
}

属性模板变量

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

引用其他属性值

malagu.yml
port: 3000
host: localhost
url: "https://${host}:${port}"

引用环境变量值

malagu.yml
password: ${env.PASSWORD}

默认值设置

malagu.yml
password: "${env.PASSWORD?:123456}"

忽略表达式计算

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

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

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

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

EL 表达式上下文

在 EL 表达式中之所以可以使用组件属性,是因为默认 EL 表达式上下文已经添加了组件属性。框架提供了相关扩展点,我们可以扩展默认的 EL 表达式上下文。只需要实现 ContextInitializer接口,并以ContextInitializer为 ID 注入到 IoC 容器即可。通过ContextInitializer接口扩展的上下文是运行时上下文,在编译期无法取到

@Component(ContextInitializer)
export class CoreContextInitializer implements ContextInitializer {
  @Autowired(JexlEngineProvider)
  protected readonly jexlEngineProvider: JexlEngineProvider<any>;

  initialize(ctx: ExpressionContext): void {
    ctx.env = { ...process.env, _ignoreEl: true };
    const jexlEngine = this.jexlEngineProvider.provide();
    jexlEngine.addTransform(
      "replace",
      (val: string, searchValue: string | RegExp, replaceValue: string) => val && val.replace(new RegExp(searchValue, "g"), replaceValue)
    );
    jexlEngine.addTransform("regexp", (pattern: string, flags?: string) => new RegExp(pattern, flags));
  }

  priority = 500;
}

在示例代码中,我们可以看到,不仅仅扩展了表达式上下文,还为 EL 表达式引擎扩展了转换函数 replaceregexp

malagu.yml
name: ${malagu['fc-adapter'].function.name|replace('-', '_')}
origin: ${{'cellbang\.com$'|regexp}}

默认提供 EL 表达式上下文

  • 应用配置属性:使用范围:编译时和运行时。例如 ${mode} ${malagu.server.port}  ${stage}
  • env:环境变量,使用范围:编译时和运行时。例如 ${env.PASSWORD}  ${{env.PASSWORD}}
  • pkg:应用程序包信息,使用范围:编译时。例如 ${pkg.name}  程序包名称 ${pkg.version}  程序包版本
  • cliContext:命令行上下文信息,使用范围:编译时。例如 ${cliContext.dev}  是否是本地运行环境

默认提供 EL 表达式转换函数

  • replace 字符串全局替换函数,使用范围:编译时和运行时。例如 ${'foo'|replace('a', 'b')}
  • regexp 字符串转正则表达式函数,如果该正则表达式需要运行时使用,务必使用 ${{}},使用范围:编译时和运行时。例如 ${{'cellbang\.com$'|regexp}}

Copyright © 2024 Zero (github@groupguanfang) 粤ICP备2023102563号