2.1 语法哲学
> **核心策略**:HTML 优先,增强属性。让 HTML 开发者感到亲切,让 Rust 开发者感到简洁。
2.1.1 三条语法原则
RML 的语法设计遵循三条原则,它们是所有语法决策的判据:
原则一:使用标准 HTML 标签
RML 不发明新标签。所有结构标签都是 HTML 开发者熟悉的:
<div>、<span>、<p>、<h1>~<h6>、<button>、<input>、<textarea>、
<ul>、<ol>、<li>、<img>、<a>、<label>
这意味着:
- ✅ 任何 HTML 编辑器都能高亮 `.rml` 文件 - ✅ 任何 XML 工具都能格式化、lint `.rml` 文件 - ✅ Web 开发者打开 `.rml` 第一秒就能读懂结构
原则二:使用标准 HTML 属性
RML 不发明新属性名。所有通用属性都是 HTML 标准:
class、id、style、placeholder、type、src、href、
value、checked、disabled、maxlength
原则三:极简指令,无框架前缀
RML 通过极简指令扩展能力,**无任何框架前缀**:
<!-- ✅ RML:无前缀,简洁 -->
<div if={is_visible}>内容</div>
<li each={item in items} key={item.id}>
<!-- ❌ 反例:如果加了前缀,就违背了 HTML 优先原则 -->
<div r:if={is_visible}>内容</div>
<li v-for="item in items" :key="item.id">
2.1.2 与 Vue / React 语法的对比
| 能力 | Vue | React | **RML** | | ------ | ------------- | -------------- | ------------------------ | | 条件渲染 | `v-if` | `{cond && <div/>}` | **`if={cond}`** | | 列表渲染 | `v-for` | `{items.map(...)}` | **`each={item in items}`** | | 双向绑定 | `v-model` | `value + onChange` | **`value={field}`** | | 显示/隐藏 | `v-show` | `style={{display}}` | **`show={cond}`** | | 事件绑定 | `@click` | `onClick` | **`on-click={fn}`** | | 插值 | `{{ var }}` | `{var}` | **`{var}`** | | 框架前缀 | `v-`、`@`、`:` | 无 | **无** |
💡 **设计要点**:RML 选择**无前缀**路线,是因为它的文件是独立的 `.rml`,不会与 HTML 混淆。前缀在 Vue 中是为了与原生 HTML 属性区分,在 RML 中没有必要。
2.1.3 增强属性:`{ }` 插值
RML 在 HTML 之上只增加一种语法:`{ }` 插值表达式。它借鉴自 React/Vue,用于把 ViewModel 数据嵌入到 UI 中。
<!-- 文本插值 -->
<p>欢迎, {user_name}</p>
<!-- 属性插值 -->
<div class={container_class}>动态类名</div>
<input value={user_name}>
<!-- 表达式插值 -->
<p>总计: {items.len()}</p>
<p>{if is_vip { "VIP" } else { "普通" }}</p>
`{ }` 内部是 Rust 表达式,编译期会进行类型检查。详见 [2.5 插值表达式](./interpolation.md)。
2.1.4 语法边界:什么不能做
为了保持 HTML 优先的简洁性,RML 明确**不支持**以下写法:
<!-- ❌ 不支持在 .rml 中写 Rust 语句 -->
<div>
{ let x = 5; x }
</div>
<!-- ❌ 不支持在 .rml 中定义函数 -->
<div>
{ fn helper() -> i32 { 42 } helper() }
</div>
<!-- ❌ 不支持在 .rml 中写复杂的逻辑控制流 -->
<div>
{ for i in 0..10 { /* 不能在插值里循环 */ } }
</div>
这些限制是**有意为之**:
- `.rml` 是声明文件,不是脚本文件 - 复杂逻辑应该放在 `.rml.rs` 的 `#[computed]` 方法中 - 这强制保持了"逻辑与表现分离"的原则
如果需要在 UI 中展示复杂计算结果,正确做法是:
// counter.rml.rs
#[computed]
pub fn display_message(&self) -> SharedString {
if self.count > 10 {
"🚀 超过十啦!".into()
} else {
"继续加油".into()
}
}
<!-- counter.rml -->
<span>{display_message}</span>
2.1.5 注释
RML 支持 HTML 风格注释:
<!-- 这是一个注释,不会渲染到 UI -->
<div>内容</div>
<!--
多行注释
可以跨越多行
-->
⚠️ **注意**:注释不会被渲染到最终 UI,但会保留在 `.rml` 文件中作为文档。RML 编译器会忽略注释内容。
2.1.6 文件结构约定
一个 `.rml` 文件的标准结构:
<!-- 1. 文件头注释(可选):说明视图用途 -->
<!-- counter.rml —— 计数器视图 -->
<!-- 2. 单一根元素(必需) -->
<div class="counter-container">
<!-- 3. 子元素 -->
<h1>{title}</h1>
<!-- 4. 绑定与指令 -->
<button on-click={increment}>+</button>
</div>
**关键规则**:
- 每个 `.rml` 文件**必须有且仅有一个根元素** - 根元素可以是任意 HTML 标签或自定义组件 - 文件名与 `.rml.rs` 中的 ViewModel 通过 `#[component]` 宏自动关联
2.1.7 小结
RML 的语法哲学可以浓缩为:
> **"HTML 标签 + HTML 属性 + `{ }` 插值 + 极简指令,无任何框架前缀。"**
这种设计让 `.rml` 文件:
- 对设计师:像 HTML 一样亲切 - 对前端工程师:像 React/Vue 一样熟悉 - 对 Rust 工程师:像声明式宏一样简洁 - 对工具链:像 XML 一样可处理
下一节 → [2.2 标签与控件映射](./tags-mapping.md)