SUI 表达式语言文档
概述
SUI(Simple User Interface)是YAO应用内置的模板处理引擎。SUI 使用 {{ }}
引用页面变量或表达式,{% %}
引用上级 HTML 元素属性值,结合 Expr 解析库(文档,源代码)提供强大的表达式处理能力。本文档详细介绍 SUI 表达式的语法、功能和使用方法,包括字面量、运算符、变量、函数以及 SUI 特定的扩展功能。
基本语法
SUI 表达式基于 Expr 语言,支持以下核心功能:
- 变量引用:使用
{{ variable }}
直接渲染变量值。 - 属性访问:通过
{{ object.key }}
或{{ array[0] }}
访问对象或数组。 - 逻辑表达式:支持算术、比较、逻辑运算以及函数调用。
- SUI 特定语法:
{{ }}
:渲染变量或表达式结果。{% %}
:引用上级 HTML 元素属性。s:if
,s:for
,s:on-click
等指令:用于条件渲染、循环和事件绑定。s:attr-
前缀:处理布尔属性(如disabled
,checked
)。
示例
<!-- 渲染变量 -->
<p>{{ user.Name }}</p>
<!-- 输出: John Doe -->
<!-- 条件渲染 -->
<div s:if="len(articles.data) > 0">有文章</div>
<div s:else>没有文章</div>
<!-- 循环渲染 -->
<ul s:for="articles.data" s:for-item="article">
<li>{{ article.title }}</li>
</ul>
<!-- 事件绑定 -->
<button s:on-click="handleClick" s:data-id="{{ id }}">点击</button>
表达式类型
SUI 表达式基于 Expr,支持多种数据类型和操作,以下为详细分类。
字面量
字面量表示固定值,支持以下类型:
- 布尔值:
true
,false
- 整数:十进制(
42
)、十六进制(0x2A
)、八进制(0o52
)、二进制(0b101010
) - 浮点数:
0.5
,.5
- 字符串:单引号(
'bar'
)、双引号("foo"
)、反引号(`Hello\nWorld`
,原始字符串) - 数组:
[1, 2, 3]
- 映射:
{a: 1, b: 2, c: 3}
- 空值:
nil
示例
{{ true }} // 输出: true
{{ 0x2A }} // 输出: 42
{{ 0.5 }} // 输出: 0.5
{{ "foo" }} // 输出: foo
{{ `Hello\nWorld` }} // 输出: Hello\nWorld
{{ [1, 2, 3] }} // 输出: [1, 2, 3]
{{ {a: 1, b: 2} }} // 输出: {a: 1, b: 2}
{{ nil }} // 输出: nil
运算符
SUI 支持多种运算符,用于处理算术、比较、逻辑、条件、成员访问、字符串操作等。
算术运算符
包括 +
, -
, *
, /
, %
(取模),^
或 **
(幂运算)。
{{ 2 + 3 }} // 输出: 5
{{ 10 % 3 }} // 输出: 1
{{ 2 ^ 3 }} // 输出: 8
比较运算符
包括 ==
, !=
, <
, >
, <=
, >=
。
{{ 5 == 5 }} // 输出: true
{{ 5 != 3 }} // 输出: true
{{ 5 < 6 }} // 输出: true
逻辑运算符
包括 not
(或 !
)、and
(或 &&
)、or
(或 ||
)。
{{ not true }} // 输出: false
{{ true and false }} // 输出: false
{{ true or false }} // 输出: true
条件运算符
包括三元运算符 ?:
、空值合并 ??
和多行 if {} else {}
。
{{ true ? 1 : 0 }} // 输出: 1
{{ isNull ?? "default" }} // 输出: default
{{ if { user.Age > 18 } else { "未成年" } }} // 输出: user.Age > 18 的结果
成员运算符
支持 .
和 []
访问字段,in
检查成员,?.
可选链,[]
数组索引(支持负索引)。
{{ user.Name }} // 输出: "John Doe"
{{ array[0] }} // 输出: 1
{{ array[-1] }} // 输出: 5
{{ "John" in ["John", "Jane"] }} // 输出: true
{{ user?.User?.Name ?? "Anonymous" }} // 输出: "Anonymous"
字符串运算符
包括 +
(拼接)、contains
、startsWith
、endsWith
。
{{ "Hello" + " World" }} // 输出: "Hello World"
{{ "Hello World" contains "World" }} // 输出: true
{{ "Hello" startsWith "He" }} // 输出: true
正则表达式运算符
matches
用于正则匹配。
{{ "hello123" matches "h.*3" }} // 输出: true
范围运算符
..
创建整数范围。
{{ 1..3 }} // 输出: [1, 2, 3]
切片运算符
[:]
用于数组切片。
{{ array[1:4] }} // 输出: [2, 3, 4]
{{ array[:3] }} // 输出: [1, 2, 3]
{{ array[3:] }} // 输出: [4, 5]
管道运算符
|
将左侧结果传递给右侧函数。
{{ user.Name | lower() | split(" ")[0] }} // 输出: "john"
变量
使用 let
声明变量,变量名以字母或下划线开头,可包含字母、数字和下划线。
{{ let x = 42; x * 2 }} // 输出: 84
{{ let name = user.Name | lower() | split(" "); "Hello, " + name[0] + "!" }} // 输出: "Hello, john!"
$env
$env
是包含所有传入变量的映射。
{{ 'user' in $env }} // 输出: true
{{ $env["user"].Name }} // 输出: "John Doe"
谓词
谓词用于函数(如 filter
, all
, any
),支持 #
或省略字段访问。
{{ filter(0..9, {# % 2 == 0}) }} // 输出: [0, 2, 4, 6, 8]
{{ filter(tweets, len(.Content) > 20) }} // 输出: [{Content: "This is a long tweet...", Size: 22}]
函数
SUI 提供丰富的内置函数,分为字符串、日期、数字、数组、映射、类型转换和位运算等类别。
字符串函数
- trim(str[, chars]):移除字符串两端的空白或指定字符。bash
{{ trim(" Hello ") }} // 输出: "Hello" {{ trim("__Hello__", "_") }} // 输出: "Hello"
- trimPrefix(str, prefix):移除字符串前缀。bash
{{ trimPrefix("HelloWorld", "Hello") }} // 输出: "World"
- upper(str):转换为大写。bash
{{ upper("hello") }} // 输出: "HELLO"
- lower(str):转换为小写。bash
{{ lower("HELLO") }} // 输出: "hello"
- split(str, delimiter[, n]):按分隔符分割字符串。bash
{{ split("apple,orange,grape", ",") }} // 输出: ["apple", "orange", "grape"] {{ split("apple,orange,grape", ",", 2) }} // 输出: ["apple", "orange,grape"]
- 其他:
splitAfter
,replace
,repeat
,indexOf
,lastIndexOf
,hasPrefix
,hasSuffix
等,详见测试页面。
日期函数
基于 Go 的 time
包,支持日期操作。
- now():返回当前时间。bash
{{ now().Year() }} // 输出: 2025(取决于当前年份)
- duration(str):将字符串转换为时间段。bash
{{ duration("1h").Seconds() }} // 输出: 3600
- date(str[, format[, timezone]]):将字符串解析为日期。bash
{{ date("2023-08-14").Year() }} // 输出: 2023 {{ date("2023-08-14 15:04:05", "2006-01-02 15:04:05", "Europe/Zurich").Hour() }} // 输出: 15
- timezone(str):设置时区。bash
{{ date("2023-08-14 00:00:00").In(timezone("Europe/Zurich")).Hour() }} // 输出: 时区调整后的小时
数字函数
包括 max
, min
, abs
, ceil
, floor
, round
。
{{ max(5, 7) }} // 输出: 7
{{ abs(-5) }} // 输出: 5
{{ ceil(1.5) }} // 输出: 2.0
数组函数
支持数组操作,如过滤、映射、排序等。
- all(array, predicate):检查所有元素是否满足谓词。bash
{{ all(tweets, {.Size < 280}) }} // 输出: true
- any(array, predicate):检查是否有元素满足谓词。bash
{{ any(tweets, {.Size > 20}) }} // 输出: true
- map(array, predicate):对每个元素应用谓词。bash
{{ map(tweets, {.Size}) }} // 输出: [11, 22]
- filter(array, predicate):过滤满足谓词的元素。bash
{{ filter(numbers, {# > 3}) }} // 输出: [4, 5]
- 其他:
one
,none
,find
,findIndex
,findLast
,findLastIndex
,groupBy
,count
,concat
,flatten
,uniq
,join
,reduce
,sum
,mean
,median
,first
,last
,take
,reverse
,sort
,sortBy
等,详见测试页面。
映射函数
- keys(map):返回映射的键数组。bash
{{ keys({a: 1, b: 2}) }} // 输出: ["a", "b"]
- values(map):返回映射的值数组。bash
{{ values({a: 1, b: 2}) }} // 输出: [1, 2]
类型转换函数
包括 type
, int
, float
, string
, toJSON
, fromJSON
, toBase64
, fromBase64
, toPairs
, fromPairs
。
{{ type(42) }} // 输出: "int"
{{ float("123.45") }} // 输出: 123.45
{{ toJSON({a: 1}) }} // 输出: "{\"a\": 1}"
{{ toBase64("Hello World") }} // 输出: "SGVsbG8gV29ybGQ="
位运算函数
包括 bitand
, bitor
, bitxor
, bitnand
, bitnot
, bitshl
, bitshr
, bitushr
。
{{ bitand(0b1010, 0b1100) }} // 输出: 8
{{ bitshl(0b101101, 2) }} // 输出: 180
其他函数
- len(v):返回数组、映射或字符串的长度。bash
{{ len("Hello") }} // 输出: 5 {{ len([1, 2, 3]) }} // 输出: 3
- get(v, index):获取数组或映射的元素。bash
{{ get([1, 2, 3], 1) }} // 输出: 2 {{ get({a: 1}, "a") }} // 输出: 1
SUI 扩展功能
SUI 提供了一些特定于其环境的扩展功能,增强了表达式的灵活性。
SUI 扩展函数
- P_(process_name, ...args):调用处理器,
process_name
为处理器名称,args
为参数。html<div>{{ P_('scripts.app.blog.site.getPostList') }}</div> <!-- 输出: 文章列表 -->
- True(value):判断值是否为真(
true
,"true"
,1
, 非零值返回true
)。bash{{ True(1) }} // 输出: true {{ True("true") }} // 输出: true
- False(value):判断值是否为假(
false
,"false"
,0
, 空字符串返回true
)。bash{{ False(0) }} // 输出: true {{ False("") }} // 输出: true
- Empty(value):判断值是否为空(空数组、空对象、空字符串返回
true
)。bash{{ Empty([]) }} // 输出: true {{ Empty("") }} // 输出: true
布尔属性处理
HTML 中的布尔属性(如 disabled
, checked
, selected
, required
)在 SUI 中通过 s:attr-
前缀处理,仅当值为真时添加属性。
<input s:attr-disabled="{{True(isDisable)}}" />
<!-- 如果 isDisable 为 true,则渲染为 <input disabled> -->
<input s:attr-checked="{{True(isCheck)}}" />
<!-- 如果 isCheck 为 true,则渲染为 <input checked> -->
注意:
- 布尔属性即使赋值为
"false"
或其他字符串,也会被视为存在。 - 使用
True()
确保属性仅在值明确为真时添加。
条件渲染
使用 s:if
, s:elif
, s:else
进行条件渲染。
<div s:if="len(articles.data) == 0">没有文章</div>
<div s:elif="len(articles.data) == 1">仅有一篇文章</div>
<div s:else>多篇文章</div>
注意:SUI 使用 len()
而非 JavaScript 的 .length
。
循环渲染
使用 s:for
和 s:for-item
进行循环。
<ul s:for="articles.data" s:for-item="article">
<li>{{ article.title }}</li>
</ul>
事件绑定
使用 s:on-<event>
绑定事件处理函数。
<button s:on-click="handleClick" s:data-id="{{ id }}">点击</button>
调试与开发
调试技巧
- 输出表达式值:使用
{{ }}
检查表达式结果。html<div>{{ user.Name }}</div> <!-- 输出: John Doe -->
- 查看环境变量:使用
$env
检查所有可用变量。html<div>$env: {{ $env }}</div>
- 检查特定变量:html
<div>articles: {{ articles }}</div>
开发命令
- 构建页面:bash
yao sui build <sui_id> <template_id>
- 监视文件变化:bash
yao sui watch <sui_id> <template_id>
注意事项
- 避免 JavaScript 语法:SUI 使用 Expr 语法,例如
len(articles.data)
而非articles.data.length
。 - 布尔属性:始终使用
True()
或False()
配合s:attr-
前缀处理布尔属性。 - 测试页面:参考
expression.html
(需配合expression.json
和expression.css
)测试表达式。 - 错误调试:若表达式未正确渲染,检查
expression.json
数据是否正确,或使用yao sui watch
实时预览。
示例
以下为综合示例,展示 SUI 表达式的实际应用:
<!-- 数据渲染 -->
<div>用户名: {{ user.Name }}</div>
<!-- 条件渲染 -->
<div s:if="user.Age > 18">成年用户</div>
<div s:else>未成年用户</div>
<!-- 循环渲染 -->
<ul s:for="articles.data" s:for-item="article">
<li>{{ article.title }} ({{ len(article.title) }} 字符)</li>
</ul>
<!-- 调用处理器 -->
<div>文章列表: {{ P_('scripts.app.blog.site.getPostList') }}</div>
<!-- 布尔属性 -->
<input type="checkbox" s:attr-checked="{{True(isCheck)}}" />
<button s:attr-disabled="{{True(isDisable)}}">提交</button>
<!-- 复杂表达式 -->
<div>
{{ let name = user.Name | lower() | split(" ")[0]; "欢迎, " + name + "!" }}
</div>
参考资源
- Expr 官方文档:https://expr-lang.org/
- Expr 源代码:https://github.com/expr-lang/expr
- 测试页面:
expression.html
(需配合expression.json
和expression.css
)