跳转到内容

2. .sharq 格式和模板指令 ​

.sharq —— 单文件组件 ​

每个 .sharq 文件都是一个 类 Vue 的 SFC,包含三个部分:

部分作用
<template>UXML 标记和指令(v-if、:text、@click 等)
<script>C# 字段、方法、Prop<T>、生命周期
<style>本组件的 USS 样式(可选 scoped)
<template> … </template>
<script>   … </script>
<style>    … </style>          <!-- global USS for this file -->
<style scoped> … </style>      <!-- isolated USS (this component only) -->

样式存放在 .sharq。 在 C# 中优先使用 <style> 中的 USS 类,而非 element.style.*。
另见 CSS 作用域 —— 对根选择器很重要。

完整例子 ​

xml
<!-- Card.sharq -->
<template>
  <ui:VisualElement $MainElement class="card">
    <ui:Label v-if="ShowTitle" :text="Title" class="card__title" />
    <ui:Label v-show="ShowSubtitle" :text="Subtitle" class="card__subtitle" />
    <ui:VisualElement v-for="item in Items" :key="item.Id" class="card__row">
      <ui:Label :text="item.Name" />
      <ui:Label :text="item.Description" />
    </ui:VisualElement>
    <ui:Button @click="OnCardClicked" class="card__button" />
    <slot name="footer" />
  </ui:VisualElement>
</template>

<script>
$using UnityEngine;
$using System.Collections.Generic;

public class CardItem
{
    public int Id;
    public string Name;
    public string Description;
}

public string Title = "Card Title";
public string Subtitle = "This is a subtitle";
public bool ShowTitle = true;
public bool ShowSubtitle = true;
public List<CardItem> Items = new()
{
    new CardItem { Id = 1, Name = "Feature A", Description = "First feature" },
    new CardItem { Id = 2, Name = "Feature B", Description = "Second feature" },
};

private void OnCardClicked()
{
    ShowSubtitle = !ShowSubtitle;
    Items.Add(new CardItem { Id = Items.Count + 1, Name = "New", Description = "Added" });
}
</script>

<style>
/* Child selectors — scoping works correctly after compile */
.card__title {
    font-size: 18px;
    color: white;
    -unity-font-style: bold;
}
.card__subtitle {
    font-size: 14px;
    color: rgba(255, 255, 255, 0.7);
    margin-top: 4px;
}
.card__row {
    flex-direction: row;
    margin-top: 8px;
}
.card__button {
    margin-top: 12px;
    height: 36px;
}
</style>

编译器会生成:

  • Card.g.cs —— C# 组件
  • Card.g.uss(以及/或 scoped/static USS)—— 来自 <style> 的样式

请编辑 .sharq 源文件,不要编辑生成的文件。


样式部分(<style>) ​

这是格式中的一等公民 —— 与 <template>、<script> 同等重要。

全局 <style> ​

为该组件编译的 USS 规则。应用 scoping 时,请使用 BEM 风格的子元素类(.card__title), 而不是根类本身:

xml
<style>
.card__title { font-size: 18px; }
</style>

<style scoped> ​

为每个选择器追加一个唯一的 .s-{hash} 类,并只把该类加到组件的根元素上 (参见 CSS Scoping):

xml
<style scoped>
.card { flex-grow: 1; }
.card:hover { opacity: 0.9; }
</style>

Scoped 适用于根/host 级别的规则。scope 类只加在根元素上,因此 scoped 的 子元素选择器(.card__title)不会匹配子元素。若要给子元素设置样式, 请使用带 BEM 类名的全局 <style>。

Root 样式与子元素样式 ​

  • Root/host 规则 → <style scoped>(.card → .card.s-hash,匹配根元素)或全局 <style>。
  • 子元素规则 → 全局 <style> 配合 BEM 类名(.card__title)—— 原样输出,匹配可靠。
  • 从外部定位该组件,或使用 :root 变量 → 伴生的 unscoped USS (Resources/SusRuntime/Card.uss),运行时自动加载。

详见 CSS 作用域。

优先用类而非 C# 的 style.* ​

应该这样做不应该这样做
<style> 中的 USS + AddToClassList / :class静态布局中的 element.style.width = 12f
修饰符类(.card--open)把相同令牌复制到 C#
用 EnableInClassList 切换状态/开关(hover、open、hidden…)element.style.display = DisplayStyle.None

只在以下情况使用 C# 的 style.*:计算自运行时数据、无法用类枚举的连续值(来自 prop 的尺寸、补间帧、从 resolvedStyle/GeometryChangedEvent 测得的位置)—— 并用 // sus:uss-impossible <原因> 注释标记该行,使异常显得是有意为之,而非遗留的临时方案。开关(display、position、flex*、align*、justify* 等)绝不应使用 style.*,无论是否有标记 —— 应始终用类。完整规则见 设计令牌 §1.4。

内联 style="…" 属性 ​

不同于 <style> 部分。属性样式被编译为静态 USS 类(去重):

xml
<ui:VisualElement style="flex-grow: 1; font-size: 24px; color: white;" />

可重用的内容优先用 <style> 部分。


$using —— 命名空间 ​

类似 C# 的 using。放在 <script> 开头;编译器将其生成到 .g.cs:

csharp
$using UnityEngine;
$using System.Collections.Generic;

$MainElement —— 根元素 ​

标记哪个元素就是组件。不加它的话,Sharq 会用空 VisualElement 包裹内容。

xml
<!-- Root = Label -->
<ui:Label $MainElement :text="Greeting" />

<!-- Root = VisualElement -->
<ui:VisualElement $MainElement class="wrapper" />

模板指令 ​

v-if —— 条件渲染 ​

元素会从层级结构中添加/移除(BindVisibility)。

xml
<ui:Label v-if="IsVisible" :text="Message" />

v-show —— 通过 sus-hidden 类隐藏 ​

元素保留在 DOM 中;切换 sus-hidden 类,它通过组件自身的 USS 被解析为 display: none —— 与上述基于类的切换相同,绝不直接写 style.display。

xml
<ui:Label v-show="IsActive" :text="Status" />

v-for —— 列表 ​

xml
<ui:VisualElement v-for="person in People" :key="person.Id" class="row">
    <ui:Label :text="person.Name" />
    <ui:Label :text="person.Role" />
</ui:VisualElement>

key-based diff(StrictVForKey)要求必须提供 :key。

:text —— 文本绑定 ​

xml
<ui:Label :text="Greeting" />

text="literal" —— 静态文本 ​

xml
<ui:Button text="Click Me" @click="OnClick" />

@click / @event —— 事件处理 ​

xml
<ui:Button @click="OnButtonClick" text="Click me" />

支持的事件:click、mouseenter、mouseleave、change。

:class —— 响应式类 ​

xml
<ui:VisualElement :class='{ "card--open": IsOpen }' />