组件
机器翻译
本页由英文原文机器翻译而来,尚未经过母语审校,欢迎在 GitHub 上提出修改。内容如有出入,以英文原文为准。
组件是 Quantum 应用的基本构件。它们把逻辑、数据处理和输出生成封装在可复用的模块化单元中。
本页的示例在每次变更时都会运行:带有 Output: 的示例按所示运行,其余的—— 需要参数的示例,以及使用卡片的页面——由 tests/docs/test_guide_components.py 运行。
基本结构
每个 Quantum 组件都遵循这种结构:
<q:component name="ComponentName" xmlns:q="https://quantum.lang/ns">
<!-- Component logic here -->
<q:return value="output" />
</q:component>Output: "output"
必需的元素
| 元素 | 说明 |
|---|---|
q:component | 根元素 |
name 属性 | 组件的名字(PascalCase) |
xmlns:q | Quantum 命名空间声明 |
简单组件
Hello World
<q:component name="HelloWorld" xmlns:q="https://quantum.lang/ns">
<q:return value="Hello, World!" />
</q:component>Output: "Hello, World!"
提前返回
q:return 结束组件:第一个运行的 q:return 就是结果,它后面的内容不会运行。 在 q:if 中,只有当它所在的分支运行时,它才会结束组件。
<q:component name="Stock" xmlns:q="https://quantum.lang/ns">
<q:set name="stock" value="0" type="number" />
<q:if condition="stock == 0">
<q:return value="Sold out" />
</q:if>
<q:return value="{stock} in stock" />
</q:component>Output: "Sold out"
在循环中则不同:每个 q:return 都会向列表中添加一项(参见组件中的循环)。
组件参数
用 q:param 接收输入:
<q:component name="Greeting" xmlns:q="https://quantum.lang/ns">
<q:param name="name" type="string" required="true" />
<q:param name="formal" type="boolean" default="false" />
<q:if condition="formal">
<q:return value="Good day, {name}." />
</q:if>
<q:else>
<q:return value="Hey {name}!" />
</q:else>
</q:component>name = Ana 时返回 "Hey Ana!";再加上 formal = true 时返回 "Good day, Ana."。没有 name 则是一个错误: Required parameter 'name' is missing。
参数的属性
| 属性 | 说明 | 示例 |
|---|---|---|
name | 参数名 | name="userId" |
type | 数据类型 | type="string" |
required | 必需参数 | required="true" |
default | 默认值 | default="10" |
类型
q:param 的 type 是 string、integer、number、decimal、boolean、 array、object、json、email、url、date、file 或 any 之一 (以及别名 text、int、long、numeric、float、double、binary、upload)。 其他任何名字都是解析错误。
不符合的值是一个指明参数的错误:类型为 number 的 age 收到 abc 时,组件以 Parameter 'age' must be a number, got 'abc' 停止;email 收到 not-an-email 时,以 Parameter 'email' must be a valid email 停止。
组件状态
用 q:set 定义内部变量。之后同名的 q:set 会替换这个值:
<q:component name="Counter" xmlns:q="https://quantum.lang/ns">
<q:set name="count" value="0" type="number" />
<q:set name="step" value="1" type="number" />
<q:set name="count" value="{count + step}" />
<q:return value="Count: {count}" />
</q:component>Output: "Count: 1"
变量校验
q:set 可以检查它保存的值——validate(email、url……)、range 和 enum:
<q:set name="email"
value="user@example.com"
validate="email" />
<q:set name="status"
type="string"
value="active"
enum="active,inactive,pending" />
<q:set name="age"
type="number"
value="200"
range="0..150" />Error: Value must be between 0 and 150
前两个通过;第三个让组件停止。不在列表中的 status 会以 Value must be one of: active, inactive, pending 让它停止,不是电子邮件的地址则以 Invalid email format 让它停止。
组件函数
用 q:function 定义可复用的逻辑,并在表达式中调用它:
<q:component name="Calculator" xmlns:q="https://quantum.lang/ns">
<q:function name="add" returnType="number">
<q:param name="a" type="number" required="true" />
<q:param name="b" type="number" required="true" />
<q:return value="{a + b}" />
</q:function>
<q:function name="multiply" returnType="number">
<q:param name="a" type="number" required="true" />
<q:param name="b" type="number" required="true" />
<q:return value="{a * b}" />
</q:function>
<q:set name="sum" value="{add(5, 3)}" />
<q:set name="product" value="{multiply(4, 7)}" />
<q:return value="5 + 3 = {sum}, 4 * 7 = {product}" />
</q:component>Output: "5 + 3 = 8, 4 * 7 = 28"
更多内容见函数。
组件中的循环
循环中的 q:return 不会结束循环:每一个都会添加一项,组件返回这个列表。
范围循环
<q:component name="Numbers" xmlns:q="https://quantum.lang/ns">
<q:loop type="range" var="i" from="1" to="5">
<q:return value="Number {i}" />
</q:loop>
</q:component>Output: ["Number 1", "Number 2", "Number 3", "Number 4", "Number 5"]
数组循环
<q:component name="Fruits" xmlns:q="https://quantum.lang/ns">
<q:set name="fruits" value='["Apple", "Banana", "Cherry"]' />
<q:loop type="array" var="fruit" items="{fruits}">
<q:return value="I like {fruit}" />
</q:loop>
</q:component>Output: ["I like Apple", "I like Banana", "I like Cherry"]
列表循环
<q:component name="Colors" xmlns:q="https://quantum.lang/ns">
<q:loop type="list" var="color" items="red,green,blue" delimiter=",">
<q:return value="Color: {color}" />
</q:loop>
</q:component>Output: ["Color: red", "Color: green", "Color: blue"]
带索引的循环
index 给位置命名,从 0 开始计数:
<q:component name="IndexedList" xmlns:q="https://quantum.lang/ns">
<q:set name="items" value='["First", "Second", "Third"]' />
<q:loop type="array" var="item" items="{items}" index="i">
<q:return value="{i + 1}. {item}" />
</q:loop>
</q:component>Output: ["1. First", "2. Second", "3. Third"]
条件
基本的 If/Else
<q:component name="AgeCheck" xmlns:q="https://quantum.lang/ns">
<q:param name="age" type="number" required="true" />
<q:if condition="age >= 18">
<q:return value="Adult" />
</q:if>
<q:else>
<q:return value="Minor" />
</q:else>
</q:component>age = 20 时返回 "Adult";15 时返回 "Minor"。
多个条件
<q:component name="Grade" xmlns:q="https://quantum.lang/ns">
<q:param name="score" type="number" required="true" />
<q:if condition="score >= 90">
<q:return value="A" />
</q:if>
<q:elseif condition="score >= 80">
<q:return value="B" />
</q:elseif>
<q:elseif condition="score >= 70">
<q:return value="C" />
</q:elseif>
<q:elseif condition="score >= 60">
<q:return value="D" />
</q:elseif>
<q:else>
<q:return value="F" />
</q:else>
</q:component>score = 85 时返回 "B";42 时返回 "F"。
数据绑定
用 {expression} 表示动态值:
简单变量
<q:set name="name" value="Alice" />
<q:return value="Hello, {name}!" />Output: "Hello, Alice!"
对象属性
<q:set name="user" type="object" value='{"name": "Bob", "age": 30}' />
<q:return value="{user.name} is {user.age} years old" />Output: "Bob is 30 years old"
表达式
<q:set name="price" value="100" />
<q:set name="quantity" value="5" />
<q:return value="Total: ${price * quantity}" />Output: "Total: $500"
字符串函数
函数以值作为参数来调用——参见函数列表:
<q:set name="text" value="hello world" />
<q:return value="{upper(text)}" />Output: "HELLO WORLD"
嵌套循环
内层循环的项会按顺序逐一放进外层循环的列表:
<q:component name="Report" xmlns:q="https://quantum.lang/ns">
<q:set name="categories" value='[
{"name": "Electronics", "items": ["Phone", "Laptop"]},
{"name": "Clothing", "items": ["Shirt", "Pants"]}
]' />
<q:loop type="array" var="category" items="{categories}">
<q:return value="Category: {category.name}" />
<q:loop type="array" var="item" items="{category.items}">
<q:return value=" - {item}" />
</q:loop>
</q:loop>
</q:component>Output: ["Category: Electronics", " - Phone", " - Laptop", "Category: Clothing", " - Shirt", " - Pants"]
在一个组件中使用另一个组件
页面通过导入另一个组件——卡片、布局——并把它写成标签来使用它。保存为 components/_parts/Card.q:
<q:component name="Card" xmlns:q="https://quantum.lang/ns">
<q:param name="title" required="true" />
<section class="card">
<h2>{title}</h2>
<q:slot />
</section>
</q:component>保存为 components/index.q:
<q:component name="Home" xmlns:q="https://quantum.lang/ns">
<q:import component="Card" from="_parts" />
<q:set name="open" value="3" type="number" />
<Card title="Open tickets: {open}">
<p>The oldest is from {'Monday'}.</p>
</Card>
</q:component>打开 / 会显示标题为 Open tickets: 3 的卡片,卡片里面是 The oldest is from Monday.
q:import在quantum.config.yaml的paths.components中查找组件,声明了from时在该文件夹中查找。名字以_开头的文件夹不会被当作页面提供,适合放这类部件。- 标签的每个属性都是组件的一个
q:param,在页面中求值:title="Open tickets: {open}"能看到页面的open。缺少必需参数是一个错误。 <Card>和</Card>之间的内容在页面的作用域中绘制,并放到组件中<q:slot />的位置。- 组件使用页面的数据源和服务,并看到同样的
session、application和request。 - 找不到的组件或失败的组件是页面的错误——绝不会是一个悄悄消失的区块。
错误
一个无法完成它所声明的事情的组件,会以说明原因的错误停止。
缺少参数
<q:component name="Ticket" xmlns:q="https://quantum.lang/ns">
<q:param name="id" type="integer" required="true" />
<q:return value="Ticket {id}" />
</q:component>Error: Required parameter 'id' is missing
不存在的变量
<q:component name="ErrorExample" xmlns:q="https://quantum.lang/ns">
<q:return value="{undefined_variable}" />
</q:component>Error: variable 'undefined_variable' is not defined
最佳实践
1. 单一职责
每个组件都应该有一个明确的用途:
<!-- Good: Focused component -->
<q:component name="UserEmail" xmlns:q="https://quantum.lang/ns">
<q:param name="email" type="email" required="true" />
<q:return value="{email}" />
</q:component>2. 使用描述性的名字
优先使用 <q:component name="ProductPriceFormatter"> 而不是 <q:component name="PF">:名字就是使用它的页面所读到的内容。
3. 为参数写文档
<!--
Formats a price with a currency code.
@param amount - The price amount (required)
@param currency - Currency code (default: USD)
-->
<q:component name="PriceFormatter" xmlns:q="https://quantum.lang/ns">
<q:param name="amount" type="decimal" required="true" />
<q:param name="currency" type="string" default="USD" />
<q:return value="{currency} {round(amount, 2)}" />
</q:component>amount = 19.999 时返回 "USD 20.0"。
4. 校验输入
<q:component name="SafeComponent" xmlns:q="https://quantum.lang/ns">
<q:param name="count" type="integer" required="true" />
<q:if condition="count < 1">
<q:return value="Error: count must be at least 1" />
</q:if>
<q:return value="{count} item(s)" />
</q:component>count = 3 时返回 "3 item(s)";-1 时返回 "Error: count must be at least 1";abc 时是错误 Parameter 'count' must be an integer, got 'abc'。