Facet 参考
Facet 参考
当主语法指南不再足够,需要更深入的 Facet 行为、上下文对象、辅助合约和渲染规则参考资料时,请使用此页面。
此页面刻意内容密集。它是以下文档的技术参考补充:
本页涵盖内容
当你需要以下详情时,请使用此参考:
- 上下文对象及其常见属性
- 页面、媒体和集合的访问模式
- 图像和地图辅助合约
- 按类别的过滤器
- 变量系统行为
- 布局级插槽
- 托管数据属性
- 媒体引用
- 渲染与安全规则
上下文对象
Facet 模板可以访问一组受控的根级对象。哪些属性有用取决于当前的渲染场景,但相同的思路在各处适用。
page
page 是当前渲染上下文中的当前页面对象。
核心页面属性
常见的标量属性包括:
page.idpage.namepage.titlepage.urlpage.httpUrlpage.pathpage.templatepage.createdpage.modifiedpage.createdStrpage.modifiedStrpage.statuspage.sortpage.numChildren
典型用法:
<h1>{{ page.title }}</h1>
<p>Updated {{ page.modified | date("F j, Y") }}</p>状态类属性
常见的布尔类属性包括:
page.isPublishedpage.isHiddenpage.isUnpublishedpage.isTrashpage.isNewpage.hasChildrenpage.isEditable
示例:
{{#if page.hasChildren}}
<p>This page has child pages.</p>
{{/if}}page.isEditable 对于仅应在当前页面编辑者可见的 UI 特别有用:
{{#if page.isEditable}}
<a href="{{ page.url }}?edit=1">Edit this page</a>
{{/if}}关系属性
Facet 常常暴露关系访问,例如:
page.parentpage.parentspage.rootParentpage.childrenpage.siblingspage.nextpage.prevpage.find
示例:
{{ page.parent.title }}
{{ page.rootParent.title }}
{{#each page.children as="child"}}
<a href="{{ child.url }}">{{ child.title }}</a>
{{/each}}媒体属性
常见的媒体相关属性包括:
page.imagespage.imagepage.files
示例:
{{#each page.images as="img"}}
<img src="{{ img.width(800).webp.url }}" alt="{{ img.description }}">
{{/each}}
{{#each page.files as="file"}}
<a href="{{ file.url }}">{{ file.filename }}</a>
{{/each}}图像对象属性
典型的图像对象属性包括:
img.urlimg.httpUrlimg.filenameimg.descriptionimg.widthimg.heightimg.extimg.filesizeimg.webp.urlimg.raw.url
文件对象属性
典型的文件对象属性包括:
file.urlfile.httpUrlfile.filenamefile.descriptionfile.extfile.filesize
图像变换
页面和查询图像对象支持链式变换:
{{ page.image.width(800) }}
{{ page.image.size(600, 400) }}
{{ page.image.size(600, 400).webp.url }}
{{ page.image.raw.url }}在循环中:
{{#each page.images as="img"}}
<img src="{{ img.size(400, 300).webp.url }}" alt="{{ img.description }}">
{{/each}}集合方法
常见的集合辅助方法包括:
firstlastcounteq(n)slice(start, length)
示例:
{{ page.images.first.url }}
{{ page.images.count }}
{{ page.images.eq(2).url }}动态字段访问
可以按名称访问自定义页面字段:
{{ page.summary }}
{{ page.body }}
{{ page.headline }}
{{ page.field name="my_custom_field" }}当当前渲染表面确实依赖页面级数据时使用此方法。不要用它来掩盖组件字段契约的薄弱之处。
本地化 URL
Facet 可能会为页面感知的多语言渲染暴露本地化 URL 帮助器:
{{ page.localUrl lang="fr" }}
{{ page.localUrl lang="default" }}user
user 表示当前用户上下文。
常见属性包括:
user.iduser.nameuser.titleuser.emailuser.displayNameuser.isLoggedinuser.isGuestuser.isSuperuseruser.language
典型用法:
{{#if user.isLoggedin}}
<span>Hello, {{ user.displayName }}!</span>
{{else}}
<a href="/login/">Log In</a>
{{/if}}pages
pages 暴露面向查询的辅助方法,用于标量或聚合输出。
常见模式包括:
pages.count selector="..."pages.first selector="..."pages.last selector="..."pages.titles selector="..." sep=", "pages.json selector="..."
示例:
<p>Total posts: {{ pages.count selector="template=blog-post" }}</p>
<p>Categories: {{ pages.titles selector="template=category" sep=", " }}</p>当你需要块循环时使用 {{#pages}}。对于标量输出使用内联的 pages.* 辅助方法。
languages / lang
languages 和 lang 暴露语言系统。
常见属性包括:
languages.countlanguages.currentlanguages.defaultlanguages.isMultiLanguagelanguages.alllanguages.{name}
示例:
{{#if languages.isMultiLanguage}}
Current language: {{ languages.current.title }}
{{/if}}在 {{#languages}} 内部,每个语言项通常暴露:
lang.idlang.namelang.titlelang.isDefaultlang.isCurrentlang.urllang.httpUrllang.locale
site
site 提供站点级别的值。
常见属性包括:
site.namesite.urlsite.httpUrlsite.httpHostsite.localesite.yearsite.assetssite.templatessite.adminUrlsite.adminProfileUrlsite.adminLogoutUrl
也可能提供类似设置的访问:
{{ site.setting key="company_name" }}
{{ site.setting key="phone" }}<a href="{{ site.adminUrl }}">Dashboard</a>
<a href="{{ site.adminProfileUrl }}">Profile</a>
<a href="{{ site.adminLogoutUrl }}">Log out</a>对稳定的全局值使用站点级设置,而不是用于应该属于变量或托管页面级对象的内容。
now
now 提供当前时间值。
常见属性包括:
now.timestampnow.datenow.datetimenow.yearnow.monthnow.day
示例:
<footer>© {{ now.year }} {{ site.name }}</footer>attr
attr 暴露传入变量的值:
{{! Usage: [[my-widget title="Hello" color="blue"]] }}
<div style="color: {{ attr.color | default("black") }}">
<h3>{{ attr.title | default("Default Title") }}</h3>
</div>attr 应保持小而明确。如果一个片段需要大型可编辑数据模型,它很可能应该是一个组件而不是变量。
loop
loop 在类似以下的循环块中可用:
{{#each}}{{#pages}}{{#languages}}
常见属性:
loop.indexloop.index1loop.firstloop.lastloop.lengthloop.evenloop.odd
示例:
{{#each page.children as="child"}}
<div class="{{#if loop.odd}}row-alt{{/if}}">
{{ loop.index1 }}. {{ child.title }}
</div>
{{/each}}this
在 {{#each}} 中,this 是当前循环项:
{{#each page.children}}
<a href="{{ this.url }}">{{ this.title }}</a>
{{/each}}this 与命名别名通常指向相同的当前项。
@index
@index 是 loop.index 的简写。
示例:
{{#each page.children as="child"}}
{{#if @index == 0}}
<h2>{{ child.title }}</h2>
{{else}}
<p>{{ child.title }}</p>
{{/if}}
{{/each}}组件根级字段
在组件模板中,当前组件实例的字段通常作为根级变量注入:
<h1>{{ title }}</h1>
{{#if showGallery}}
<div class="gallery">
{{#each images as="img"}}
<img src="{{ img.url }}">
{{/each}}
</div>
{{/if}}这也是组件模板感觉比通用页面级模板更简单的原因之一。
如果自定义字段名会与核心上下文对象冲突,应避免随意重用保留名称,例如:
pagepagesuserlanguageslangsitenowattrloopthis@index
组件字段辅助参考
Facet 支持若干组件专用的辅助合约,这些合约与通用的 {{ expr }} 输出并不相同。
单图像字段辅助
单个 image 字段通常支持:
{field}{field.raw}{field.webp}{field.name}{field.description}{field.width(N)}{field.width(N).webp}{field.height(N)}{field.height(N).webp}{field.size(W,H)}{field.size(W,H).webp}
示例:
<img src="{hero.width(1200).webp}" alt="{hero.description}">
<a href="{photo.raw}" download>Download original</a>多图像字段辅助
在 {{#each gallery}} 内,images 项通常支持:
{{this}}{{this.raw}}{{this.webp}}{{this.name}}{{this.description}}{{this.width(N)}}{{this.width(N).webp}}{{this.height(N)}}{{this.height(N).webp}}{{this.size(W,H)}}{{this.size(W,H).webp}}
示例:
{{#each gallery}}
<figure>
<img src="{{this.width(600).webp}}" alt="{{this.description}}">
<figcaption>{{this.name}}</figcaption>
</figure>
{{/each}}地图字段辅助
map 字段通常支持:
{field}{field.width(N)}{field.height(N)}{field.size(W,H)}
示例:
{officeLocation}
{officeLocation.width(600)}
{officeLocation.size(800,500)}仅将 map 用于结构化位置数据,而不是纯地址文本。
托管嵌入合约
托管工作流嵌入通常使用:
data-form-embeddata-review-embed
示例:
<div data-form-embed="enterprise-demo-request"></div>
<div data-review-embed="customer-success-reviews"></div>
<div data-form-embed="{contactForm}"></div>
<div data-review-embed="{serviceReview}"></div>对于模板拥有的工作流使用固定 id,对于作者可选的工作流使用基于字段的值。
过滤器参考
过滤器在输出之前转换值。最重要的规则很简单:
- 使用过滤器进行格式化
- 不要使用过滤器来掩盖薄弱的对象模型
字符串过滤器
常见的字符串过滤器包括:
escape/erawupperlowercapitalizetrimtruncatenl2brstriptagsreplacesplitslicepadwrapslug
示例:
{{ page.title | upper }}
{{ page.body | striptags | truncate(200, "...") }}
{{ text | nl2br }}
{{ title | slug }}默认值过滤器
default 在输入缺失或类似 null 的情况下提供回退值(在支持的契约中):
{{ page.subtitle | default("Untitled") }}如果确切的空字符串行为很重要,优先使用显式三元运算。
数字过滤器
常见的数字过滤器包括:
numbercurrencyabsroundceilfloor
示例:
{{ 1234567 | number(0, ".", ",") }}
{{ price | currency("$", 2) }}
{{ ratio | round(2) }}日期过滤器
常见的日期过滤器包括:
datedatetimerelative
示例:
{{ page.created | date("F j, Y") }}
{{ page.modified | datetime("Y-m-d H:i") }}
{{ page.modified | relative }}URL 过滤器
常见的 URL 相关过滤器包括:
url_encodeurl
示例:
{{ page.title | url_encode }}
{{ website | url }}JSON 过滤器
使用 json 输出安全的 JSON:
{{ data | json }}当模板需要在 HTML 或脚本导向输出中包含结构化数据时,这很有用。
数组与集合过滤器
常见的集合过滤器包括:
joinfirstlastcountlengthreversesortpluckuniquebatchkeysvaluesmerge
示例:
{{ tags | pluck("title") | join(", ") }}
{{ page.children | count }}
{{ items | batch(4) }}图像过滤器
常见的图像过滤器:
size
示例:
{{ page.image | size(600, 400) }}哈希过滤器
常见的哈希过滤器:
md5
示例:
{{ user.email | md5 }}变量系统
变量是可重用的模板片段,可嵌入 FaceFlow 渲染支持的任何位置。
包含变量
基本包含:
[[my-variable]]带属性:
[[my-widget title="Hello World" color="blue" count="5"]]变量结构
变量通常包含:
- HTML 模板
- 可选的 CSS
- 可选的 JS
- 可选的属性
- 缓存模式
缓存模式
常见模式包括:
staticdynamicauto
使用建议:
- 当输出大体上适合缓存时使用
static - 当输出取决于用户或请求敏感的运行时状态时使用
dynamic - 当引擎应根据变量行为选择时使用
auto
嵌套变量
变量可以包含其他变量:
<footer>
[[copyright-notice year="2026"]]
[[social-links]]
</footer>使用嵌套以提高重用性和可读性,而不是创建深度隐藏的依赖链。
布局级语法
面向布局的模板通常依赖少数结构性占位符:
{content}{header}{footer}{siteComponents}
示例:
<body>
{header}
{siteComponents}
<main>
{content}
</main>
{footer}
</body>这些占位符属于布局和外壳组合,而不是普通的段落渲染。
数据属性
常见的运行时相关数据属性包括:
data-form-embeddata-review-embeddata-fb-mapdata-fb-map-fielddata-component-*data-form-ajaxdata-variabledata-loading
当运行时契约明确记录这些属性时使用它们。不要随意发明平行属性。
媒体引用
某些 FaceFlow 渲染上下文可能暴露托管媒体引用模式,例如:
@image:filename@file:filename
这些是托管资源的实现端表示。在创作模板时,优先使用文档化的辅助或对象输出形式,而不是依赖原始存储引用。
渲染流水线
一个实用的 Facet 渲染顺序通常如下:
- 加载所属的 FaceFlow 对象
- 解析当前渲染上下文
- 注入根级字段和上下文对象
- 评估字段辅助合约
- 评估 Facet 表达式和块辅助
- 解析变量嵌入
- 解析托管运行时标记,如表单、评论或地图
- 返回最终输出
重要的设计教训是组件字段简写、Facet 表达式、变量嵌入和托管运行时标记是相关但不相同的阶段。
安全模型
Facet 被设计为受治理的模板层,而不是原始的代码执行环境。
关键规则:
- 不允许任意的服务器端代码执行
- 根对象受控
- 过滤器和块类型是白名单化的
- 查询行为受到约束
- 原始输出应当有意使用
把它视作沙盒化的模板语言,而非备用脚本引擎。
实用设计规则
- 偏好清晰的对象边界而非聪明的模板逻辑
- 保持模板逻辑浅显
- 对于片段使用变量,对于段落契约使用组件
- 当查询和分页是契约的一部分时使用列表
- 在使用字段辅助语法前对照字段参考进行检查
- 在引入延迟渲染之前审查缓存敏感内容
示例手册模式
导航菜单
<nav>
<ul>
{{#pages selector="parent=1, sort=sort" as="item"}}
<li><a href="{{ item.url }}">{{ item.title }}</a></li>
{{/pages}}
</ul>
</nav>博客文章列表
{{#pages selector="template=blog-post, sort=-created, limit=6" as="post"}}
<article>
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
<p>{{ post.body | striptags | truncate(250) }}</p>
</article>
{{else}}
<p>No blog posts yet.</p>
{{/pages}}多语言切换
{{#if languages.isMultiLanguage}}
{{#languages as="lang"}}
<a href="{{ lang.url }}">{{ lang.title }}</a>
{{/languages}}
{{/if}}面包屑
{{#each page.parents as="p"}}
<a href="{{ p.url }}">{{ p.title }}</a> /
{{/each}}
<span>{{ page.title }}</span>缓存安全的用户欢迎
{{#deferred skeleton-width="10rem" skeleton-height="2rem"}}
{{#if user.isLoggedin}}
<span>{{ user.displayName }}</span>
{{else}}
<a href="/login/">Sign In</a>
{{/if}}
{{/deferred}}常见问题
何时应使用 {{#pages}} 而不是 {{#each}}?
- 当数据已存在于当前上下文时使用
{{#each}} - 当模板必须查询数据时使用
{{#pages}}
{{ }} 与 {{{ }}} 有何区别?
{{ ... }}是转义输出{{{ ... }}}是原始输出
默认使用转义输出。
我可以嵌套循环吗?
可以,但仅当对象模型仍然可读时。如果嵌套循环是在补偿薄弱的内容契约,请停止并重新审视设计。
为什么某字段返回 null 或空输出?
常见原因:
- 上下文错误
- 字段名错误
- 对当前对象使用了错误的语法族
- 期望字段支持的辅助但该字段不支持
我应如何调试模板问题?
按以下顺序开始:
- 确认所属对象契约
- 确认正确的语法族
- 确认可用的上下文对象
- 将模板缩减为最小可复现片段
- 然后从那里重建