Web Device 没有一份脱离上下文、永恒不变的 component catalog。component 来自分层 source,精确 contract 属于目标 runtime/theme/site 中的 manifest.json 与 renderer implementation。
Sample 合同(layout 路径)
本页是 layout-component sample 路径,不是 Markdown directive 的 live 页。layout component 只有在 layout.aup(或 named layout)挂进页面壳时才会渲染。下方表格与 fence 是可复制的作者 source;真实渲染表面是已经挂载该 component 的站点页面,而不是在本文件里再做一个假 HTML mock。
| 字段 | 值 |
|---|---|
| 用途 | 用明确 empty state 解析并编写 theme/site layout component |
| 边界 | 不是 Markdown directive;不是 AUP document primitive;不发明 manifest 以外的 props |
| 作者入口 | layout.aup / named layouts/*.aup · component … component=… |
| 表面 | layout(及窄屏);使用该 component 的站点页面 |
| 公共状态 | 本站实际挂载的 component 为 released |
| Fallback / 失败 | 空 items → emptyText 行(或 emptyText="" 时什么也不渲染);面包屑 props 全空 → 无 nav |
| 验证 | Manifest 路径 · 下列 live 站点路由 · arc --version = 2.0.0-beta.32 |
解析层次
当前 runtime 会按以下层次组装 component:
- runtime surface;
themes/default/components;- 可选 active theme overlay;
- site 的
.web/components。
后面的同名文件覆盖前面的文件。这让 site 能窄范围覆盖已提供 component,但也意味着旧 article 中提到的 component 名称不自动成为每个 theme/version 的可移植合同。
三种 component surface 要分开
| surface | 什么能证明它的 contract | 当前文档入口 |
|---|---|---|
| 内容对象中的 Markdown | content-reader 语法与实际 document result | Web Device 内容 Markdown |
| 某个 target 上的 AUP primitive | 该 target 的 renderer、capability 边界与验收 | 把 target-specific 证据与本 Web layout reference 分开 |
.web 或 theme component | 实际解析到的 manifest、renderer、layout section 与页面结果 | 本页及使用与覆盖组件 |
不要把一个 surface 的例子抬升为另一个 surface 的 contract。Markdown card 不是 .web component API;被 AUP parser 接受的 primitive 也不会自动成为 layout section。
content-card(theme + site 覆盖)
解析: 默认 theme themes/default/components/content-card/ · 本站可通过 blocklets/arcblock/.web/components/content-card/ 覆盖。
Manifest props(作者侧): title、description、eyebrow、items、basePath、variant、readMoreHref、emptyText、headingLevel、itemHeadingLevel。
items 项常见字段为 title / slug / excerpt / category(或 meta.*)。没有 slug 表示卡片不是目的地链接(非链接外壳,而不是死的 /#/ 锚点)。
作者 source(layout 片段)
component reading slot=featured-reading component=content-card \
variant=fleet \
title="精选" \
description="手工挑选的条目" \
basePath="/$locale/" \
emptyText="No content has been published yet." \
items=[{title: "示例", category: "Guide", excerpt: "简短说明", slug: "articles/example"}]空列表且给出明确文案:
component reading slot=featured-reading component=content-card \
title="精选" \
emptyText="No content has been published yet." \
items=[]当另一个 surface 已经负责列表外壳(例如搜索结果)时,传 emptyText="",使本 component 在空列表时什么也不渲染,避免第二条 empty 文案。
真实渲染表面(不是本 docs 正文)
| 证明方式 | 说明 |
|---|---|
挂载 content-card 的首页 / 产品 landing | 例如 /zh/、pages/*/layout.aup 下的产品页 |
| Empty state | 仅当 items 为空 且 emptyText 非空时可见 |
本 Markdown 页不 live 渲染 content-card;在这里用假截图冒充会破坏 live-example 合同。
失败与限制
| 情况 | 预期结果 |
|---|---|
items=[] 且默认 emptyText | 一行 empty-state(除非覆盖,默认 “No content has been published yet.”) |
items=[] 且 emptyText="" | 无 empty-state 节点 |
无 slug 的 item | 非链接卡片外壳 |
| 发明 manifest 中不存在的 prop | 不可移植 — 以目标 manifest.json / render.js 为准 |
page-breadcrumb(站点 component)
解析: 仅站点 blocklets/arcblock/.web/components/page-breadcrumb/(不是 default theme 的通用 catalog 条目)。
Manifest props: items([{ label, href }] — href 不带 locale 前缀;最后一项为当前页)、currentLabel(末尾 crumb,常用 $meta.name)、collection(目前:events,供无法解析 i18n block 的 layout 使用)。
作者 source(layout 片段)
来自本站活动详情 layout(pages/events/[slug]/layout.aup):
component crumb slot=purpose component=page-breadcrumb \
items=[{label: "活动", href: "/events/"}] \
currentLabel=$meta.namecurrentLabel 必须是顶层 prop,因为 $meta.* 只在 prop 自身顶层解析。若需要 live 页面标题,不要只把 $meta.name 嵌在 map 条目里。
真实渲染表面
| 路由形态 | 说明 |
|---|---|
/zh/events/<slug>/(及 en) | 回到「活动」+ 当前标题 |
挂载 page-breadcrumb 的产品 / glossary 详情 layout | 同一 component,不同 items |
失败与限制
| 情况 | 预期结果 |
|---|---|
无 items、无 collection、无 currentLabel | component 返回空 HTML(无 nav) |
最后一截 crumb 上仍写 href | 最后一截仍渲染为当前页(非链接) |
仅靠 named layout 的 i18n 提供标签 | 优先 collection 或显式 items — named layout 不会像 page layout 那样解析 layout i18n |
使用前审阅 component
- 在目标 resolution path 中找到它。
- 阅读它的
manifest.json了解声明 contract,再读render.js了解实际接受的 props/slots。 - 对可选 list/media 给出明确 empty state。
- 本地运行站点,在每个支持的 locale 和 viewport 中查看真实页面。
不要只因 manifest 不方便读就把 component implementation 粘进页面。输入 contract 不清晰时,先从目标 manifest 与 runtime 确认;不要发明一组 props。
验证证据
| 项 | 值 |
|---|---|
| 基线 | 2026-08-12 · arc --version → 2.0.0-beta.32 |
| 能力行 | docs/aup/capability-matrix.md (internal, non-routed) · content-card · page-breadcrumb |
| Manifest / render | .web/components/content-card/{manifest.json,render.js} · .web/components/page-breadcrumb/{manifest.json,render.js} · theme default content-card |
| Layout 源 | 例如 pages/events/[slug]/layout.aup、pages/index/layout.aup、产品 layout.aup |
| 本文档路由(合同正文) | /zh/docs/web-device/built-in-components/ · /en/docs/web-device/built-in-components/ |
| Live layout 证明 | arc service start --blocklet blocklets/arcblock --port 4900 后打开活动详情与首页(或产品页) |
| 验收 | 面包屑/列表卡片出现在这些 layout 页;empty-state 规则与表一致;本 docs 页对 layout component 只提供 source |
编写 site-owned component 见 使用与覆盖组件;AUP tree 与 Web layout 的边界见 AUP layout 边界。