配置文件
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-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
下面的都是后端属性,其他的为公共属性。
前端属性示例
frontend:
malagu:
foo: bar
后端属性示例
backend:
malagu:
foo: bar
公共属性示例
malagu:
foo: bar
还有一种维度的划分:
- 编译时属性
- 运行时属性
属性优先级规则
泛化原则
泛化的属性优先级低(以下规则优先级由高到底):
- 前端(后端)属性 > 公共属性
- 模式属性文件 > 公共属性文件
依赖原则
被依赖的组件属性优先级低:
- 依赖者属性 > 被依赖者属性
当以上两个原则存在冲突时:
- 泛化原则 > 依赖原则
属性规范
- 前后端代码都需要访问的属性设计成公共属性
- 公共代码只访问公共属性
- 属性层级设计:业务名.组件名.属性
- 属性命名规则:采用小驼峰(推荐)
- 属性最好提供默认值
在配置文件中使用 EL
在 Malagu 框架中,使用 Yaml 文件配置组件属性。为了让属性普通值与 EL 表达式区别开来,Malagu 使用 ${}
和${{}}
包裹 EL 表达式。一个属性值可以包含多个 EL 表达式和字符串的组合,并且 EL 表达式还可以嵌套使用。
${}
编译时 EL 表达式,在项目编译构建时计算${{}}
运行时 EL 表达式,在项目运行时计算
引用其他属性值
port: 3000
host: localhost
url: "https://${host}:${port}"
引用环境变量值
password: ${env.PASSWORD}
默认值设置
password: "${env.PASSWORD?:123456}"
忽略表达式计算
如下属性配置,因为 test 节点存在属性 _ignoreEl
为 true
,则 test 节点下的属性或者属性的属性的值不在进行表达式计算。
test:
a: '${b > 0 ? true: false}'
_ignoreEl: true
上面的方案是对某个一个节点进行表达式计算忽略,如何只对单一属性值就行表达式忽略呢?我们可以通过转义符实现。如下:
test:
a: '\\${b > 0 ? true: false}'
运行时表达式
上面的写法是在项目编译期间计算表达式的值,如果您需要在应用运行时取运行时的环境变量的话,如下:
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} />;
}
属性模板变量
属性值可以使用表达式来引用其他的属性值或者环境变量值。
引用其他属性值
port: 3000
host: localhost
url: "https://${host}:${port}"
引用环境变量值
password: ${env.PASSWORD}
默认值设置
password: "${env.PASSWORD?:123456}"
忽略表达式计算
如下属性配置,因为 test 节点存在属性 _ignoreEl
为 true,则 test 节点下的属性或者属性的属性的值不在进行表达式计算。
test:
a: '${b > 0 ? true: false}'
_ignoreEl: true
上面的方案是对某个一个节点进行表达式计算忽略,如何只对单一属性值就行表达式忽略呢?我们可以通过转义符实现。如下:
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 表达式引擎扩展了转换函数 replace
和 regexp
。
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}}