/ 高级 / 模板引擎

MoPress 内置了一套简单、轻量的模板引擎,负责将页面内容与站点的整体 HTML 结构结合起来。本节介绍模板的语法与渲染上下文。

模板被解析为一棵由以下节点类型组成的语法树 TemplateNode


///|
/// A single node in a parsed template's AST.
pub(all) enum TemplateNode {
  /// A literal, verbatim text fragment.
  Text(String)
  /// A variable reference, to be substituted with its value from the
  /// rendering context.
  Variable(String)
  /// A conditional block: the variable name to test, the nodes to render
  /// when it is truthy, and the nodes to render otherwise.
  If(String, Array[TemplateNode], Array[TemplateNode])
  /// A loop block: the variable name (expected to hold a `Value::Array`)
  /// to iterate over, and the nodes to render once per element.
  For(String, Array[TemplateNode])
  /// A reference to another template file to be rendered inline at this
  /// position.
  Partial(String)
} derive(Eq, Debug)

其语法参考自 Hakyll。

<title>$title$</title>

支持点语法:

<div>$item.title$</div>

$if(repository)$
<a href="$repository$">查看源码</a>
$endif$

$else$ 分支:

<title>
  $if(title)$
    $title$ - $site_title$
  $else$
    $site_title$
  $endif$
</title>

<ul>
$for(authors)$
  <li>$item.name$ <$item.email$></li>
$endfor$
</ul>

在循环作用域内通过 $item$ 访问当前循环项。

$partial("path/header.html")$

$partial("...")$ 会引入另一份模板文件,并将其渲染结果内联到当前位置,便于拆分和复用公共的页面片段,如页头、页脚。

Caution

谨慎使用,注意循环引用。

模板变量的值使用 Value 类型表示:


///|
/// A dynamically-typed value available to templates as part of their
/// rendering context.
pub(all) enum Value {
  String(String)
  Number(Double)
  Bool(Bool)
  Array(Array[Value])
  Object(Map[String, Value])
} derive(Eq, Debug)

Array 类型的值可以配合 $for$ 循环使用;Object 类型的值可以配合点号访问其字段,如上面循环示例中的 item.title

""false[]、未定义值(宽松模式)会被条件判断为假,其他值均为真。

pub fn apply_template(template : Array[TemplateNode], context : Map[String, Value]) -> String

pub fn apply_template_strict(
  template : Array[TemplateNode],
  context : Map[String, Value],
) -> String raise TemplateRenderError

pub fn[T : Show] parse_template(input : T) -> Array[TemplateNode] raise TemplateParseError

parse_template 把模板源码解析为 Array[TemplateNode]apply_templateapply_template_strict 分别对应下面要说的宽松、严格两种渲染模式。

模板渲染时能够访问到的所有变量,来自当前 Itemvars。在 Book 模式下,vars 中通常包含站点的基础信息(如 site_titlesite_description)、元数据中声明的字段,以及导航相关信息(如 breadcrumbprevnextsection)。在 Site 模式下,vars 的内容完全取决于处理管线中调用了哪些 step,请阅读 组合 Steps 中关于模板变量的详细说明。

模板渲染有两种模式:

  • 宽松模式apply_template):渲染过程中遇到问题,如引用了不存在的变量,会跳过对应的模板节点、以空内容代替,然后继续渲染文档的其余部分,不会导致整个渲染失败。

  • 严格模式apply_template_strict):一旦渲染中遇到任何问题,会立即中止并抛出具体的错误。对应的错误类型 TemplateParseError 定义如下:


///|
/// Errors that can occur while rendering a parsed template against a
/// variable context, as raised by `apply_template_strict`.
pub suberror TemplateRenderError {
  /// A variable referenced by the template was not present in the
  /// rendering context.
  UndefinedVariableError(String)
  /// A `for` loop referenced a variable whose value was not a `Value::Array`.
  NonArrayForLoopError(String)
  /// A referenced partial template could not be loaded (e.g. its file
  /// could not be read).
  PartialLoadError(String)
  /// A referenced partial template could not be parsed.
  PartialParseError(String)
  /// A partial template referenced itself, directly or transitively,
  /// resulting in unbounded recursion.
  RecursivePartialError(String)
} derive(Eq, Debug)

解析阶段的错误则是另一种独立的错误类型 TemplateRenderError


///|
/// Error raised when a template's source cannot be parsed into a valid
/// template AST. The associated `String` carries a human-readable
/// description of the parse failure.
pub suberror TemplateParseError {
  TemplateParseError(String)
} derive(Eq, Debug)

在 Book 模式中,load_and_apply_template 提供了 strict 参数用于在两种模式间切换,默认使用宽松模式。开发阶段建议开启严格模式以尽早暴露问题,正式发布时可以视情况切换回宽松模式,避免个别页面因为模板问题而导致整个构建失败。