Facet 参考

Facet 参考

当主语法指南不再足够,需要更深入的 Facet 行为、上下文对象、辅助合约和渲染规则参考资料时,请使用此页面。

此页面刻意内容密集。它是以下文档的技术参考补充:

本页涵盖内容

当你需要以下详情时,请使用此参考:

  • 上下文对象及其常见属性
  • 页面、媒体和集合的访问模式
  • 图像和地图辅助合约
  • 按类别的过滤器
  • 变量系统行为
  • 布局级插槽
  • 托管数据属性
  • 媒体引用
  • 渲染与安全规则

上下文对象

Facet 模板可以访问一组受控的根级对象。哪些属性有用取决于当前的渲染场景,但相同的思路在各处适用。

page

page 是当前渲染上下文中的当前页面对象。

核心页面属性

常见的标量属性包括:

  • page.id
  • page.name
  • page.title
  • page.url
  • page.httpUrl
  • page.path
  • page.template
  • page.created
  • page.modified
  • page.createdStr
  • page.modifiedStr
  • page.status
  • page.sort
  • page.numChildren

典型用法:

<h1>{{ page.title }}</h1>
<p>Updated {{ page.modified | date("F j, Y") }}</p>

状态类属性

常见的布尔类属性包括:

  • page.isPublished
  • page.isHidden
  • page.isUnpublished
  • page.isTrash
  • page.isNew
  • page.hasChildren
  • page.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.parent
  • page.parents
  • page.rootParent
  • page.children
  • page.siblings
  • page.next
  • page.prev
  • page.find

示例:

{{ page.parent.title }}
{{ page.rootParent.title }}

{{#each page.children as="child"}}
  <a href="{{ child.url }}">{{ child.title }}</a>
{{/each}}

媒体属性

常见的媒体相关属性包括:

  • page.images
  • page.image
  • page.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.url
  • img.httpUrl
  • img.filename
  • img.description
  • img.width
  • img.height
  • img.ext
  • img.filesize
  • img.webp.url
  • img.raw.url

文件对象属性

典型的文件对象属性包括:

  • file.url
  • file.httpUrl
  • file.filename
  • file.description
  • file.ext
  • file.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}}

集合方法

常见的集合辅助方法包括:

  • first
  • last
  • count
  • eq(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.id
  • user.name
  • user.title
  • user.email
  • user.displayName
  • user.isLoggedin
  • user.isGuest
  • user.isSuperuser
  • user.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

languageslang 暴露语言系统。

常见属性包括:

  • languages.count
  • languages.current
  • languages.default
  • languages.isMultiLanguage
  • languages.all
  • languages.{name}

示例:

{{#if languages.isMultiLanguage}}
  Current language: {{ languages.current.title }}
{{/if}}

{{#languages}} 内部,每个语言项通常暴露:

  • lang.id
  • lang.name
  • lang.title
  • lang.isDefault
  • lang.isCurrent
  • lang.url
  • lang.httpUrl
  • lang.locale

site

site 提供站点级别的值。

常见属性包括:

  • site.name
  • site.url
  • site.httpUrl
  • site.httpHost
  • site.locale
  • site.year
  • site.assets
  • site.templates
  • site.adminUrl
  • site.adminProfileUrl
  • site.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.timestamp
  • now.date
  • now.datetime
  • now.year
  • now.month
  • now.day

示例:

<footer>&copy; {{ 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.index
  • loop.index1
  • loop.first
  • loop.last
  • loop.length
  • loop.even
  • loop.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

@indexloop.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}}

这也是组件模板感觉比通用页面级模板更简单的原因之一。

如果自定义字段名会与核心上下文对象冲突,应避免随意重用保留名称,例如:

  • page
  • pages
  • user
  • languages
  • lang
  • site
  • now
  • attr
  • loop
  • this
  • @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-embed
  • data-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 / e
  • raw
  • upper
  • lower
  • capitalize
  • trim
  • truncate
  • nl2br
  • striptags
  • replace
  • split
  • slice
  • pad
  • wrap
  • slug

示例:

{{ page.title | upper }}
{{ page.body | striptags | truncate(200, "...") }}
{{ text | nl2br }}
{{ title | slug }}

默认值过滤器

default 在输入缺失或类似 null 的情况下提供回退值(在支持的契约中):

{{ page.subtitle | default("Untitled") }}

如果确切的空字符串行为很重要,优先使用显式三元运算。

数字过滤器

常见的数字过滤器包括:

  • number
  • currency
  • abs
  • round
  • ceil
  • floor

示例:

{{ 1234567 | number(0, ".", ",") }}
{{ price | currency("$", 2) }}
{{ ratio | round(2) }}

日期过滤器

常见的日期过滤器包括:

  • date
  • datetime
  • relative

示例:

{{ page.created | date("F j, Y") }}
{{ page.modified | datetime("Y-m-d H:i") }}
{{ page.modified | relative }}

URL 过滤器

常见的 URL 相关过滤器包括:

  • url_encode
  • url

示例:

{{ page.title | url_encode }}
{{ website | url }}

JSON 过滤器

使用 json 输出安全的 JSON:

{{ data | json }}

当模板需要在 HTML 或脚本导向输出中包含结构化数据时,这很有用。

数组与集合过滤器

常见的集合过滤器包括:

  • join
  • first
  • last
  • count
  • length
  • reverse
  • sort
  • pluck
  • unique
  • batch
  • keys
  • values
  • merge

示例:

{{ 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
  • 可选的属性
  • 缓存模式

缓存模式

常见模式包括:

  • static
  • dynamic
  • auto

使用建议:

  • 当输出大体上适合缓存时使用 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-embed
  • data-review-embed
  • data-fb-map
  • data-fb-map-field
  • data-component-*
  • data-form-ajax
  • data-variable
  • data-loading

当运行时契约明确记录这些属性时使用它们。不要随意发明平行属性。

媒体引用

某些 FaceFlow 渲染上下文可能暴露托管媒体引用模式,例如:

  • @image:filename
  • @file:filename

这些是托管资源的实现端表示。在创作模板时,优先使用文档化的辅助或对象输出形式,而不是依赖原始存储引用。

渲染流水线

一个实用的 Facet 渲染顺序通常如下:

  1. 加载所属的 FaceFlow 对象
  2. 解析当前渲染上下文
  3. 注入根级字段和上下文对象
  4. 评估字段辅助合约
  5. 评估 Facet 表达式和块辅助
  6. 解析变量嵌入
  7. 解析托管运行时标记,如表单、评论或地图
  8. 返回最终输出

重要的设计教训是组件字段简写、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 或空输出?

常见原因:

  • 上下文错误
  • 字段名错误
  • 对当前对象使用了错误的语法族
  • 期望字段支持的辅助但该字段不支持

我应如何调试模板问题?

按以下顺序开始:

  1. 确认所属对象契约
  2. 确认正确的语法族
  3. 确认可用的上下文对象
  4. 将模板缩减为最小可复现片段
  5. 然后从那里重建

相关