空白处理

在运行中,模板中的 空白 处理在某种程度上来说是纠缠所有模板引擎的一个问题。

我们来看这个模板。我已经用颜色标记了模板中的组件: 文本插值FTL 标签.。 使用 [BR] 来想象 换行

<p>List of users:[BR]
<#assign users = [{"name":"Joe",        "hidden":false},[BR]
                  {"name":"James Bond", "hidden":true},[BR]
                  {"name":"Julia",      "hidden":false}]>[BR]
<ul>[BR]
<#list users as user>[BR]
  <#if !user.hidden>[BR]
  <li>${user.name}[BR]
  </#if>[BR]
</#list>[BR]
</ul>[BR]
<p>That's all.

如果 FreeMarker 能按照规则输出所有的 文本,那将会输出:

<p>List of users:[BR]
[BR]
<ul>[BR]
[BR]
  [BR]
  <li>Joe[BR]
  [BR]
[BR]
  [BR]
[BR]
  [BR]  
  <li>Julia[BR]
  [BR]
[BR]
</ul>[BR]
<p>That's all.

这里有太多的不想要的空格和换行了。幸运的是,HTML和XML对空白都不是敏感的, 但是这么多多余的空白是很令人头疼的,而且处理后的HTML文件大小增加也是没必要的。 当然,对于空白敏感方式的输出这依旧是个大问题。

FreeMarker 提供下面的工具来处理这个问题:

  • 忽略某些模板文件的空白的工具 (解析阶段空白就被移除了)

    • 剥离空白:这个特性会自动忽略在FTL标签周围多余的空白。 这个特性可以通过模板来随时启用和禁用。

    • 微调指令:trtlt。使用这些指令可以明确地告诉 FreeMarker 去忽略某些空白。 可以阅读 参考手册 来获取更多信息。

    • ftl 参数 strip_text:这将从模板中删除所有顶级文本。 对于只包含定义宏的模板来说很有用(还有其它一些没有输出的指令), 因为它可以移除宏定义和其他顶级指令中的换行符, 这样可以提高模板的可读性。

  • 从输出中移除空白的工具 (移除临近的空白)

    • compress 指令。

剥离空白

如果对模板开启这个特性,那么它就会自动忽略 (也就是不在输出中打印出来)两种典型的多余空白:

  • 缩进空白和在行末尾的尾部空白(包括换行符)在只包含FTL标签的行中会被忽略 (比如 <@myMacro/>, <#if ...>)和FTL注释(如 <#-- blah -->), 除了被忽略的空白本身。例如,如果一行只包含一个<#if ...>, 那么在标签前面的缩进和标签后面的换行符将会被忽略。 但是,如果这行上包含<#if ...>x, 那么空白就不会被忽略,因为这个x不是FTL标签。请注意,根据这些规则, 一行上包含<#if ...><#list ...>,空白就会被忽略, 而一行上有<#if ...> <#list ...>这样的就不会, 因为在两个FTL标签之间的空白是嵌入的空白,而不是缩进的或尾部空白。

  • 加在下面这些指令之间的空白会被忽略:macrofunctionassigngloballocalftlimport, 但也是指令之间仅仅只有一个空白或FTL注释。实际应用中, 它意味着你可以在宏定义和参数定义之间放置空行,因为行间距是为了更好的可读性, 不包括在输出中打印不必要的空行(换行符)。

使用了剥离空白后,上面那个例子中的输出就会是:

<p>List of users:[BR]
<ul>[BR]
  <li>Joe[BR]
  <li>Julia[BR]
</ul>[BR]
<p>That's all.

这是因为在剥离之后,模板就变成这样了,被忽略的文本没有被标色

<p>List of users:[BR]
<#assign users = [{"name":"Joe",        "hidden":false},[BR]
                  {"name":"James Bond", "hidden":true},[BR]
                  {"name":"Julia",      "hidden":false}]>[BR]
<ul>[BR]
<#list users as user>[BR]
  <#if !user.hidden>[BR]
  <li>${user.name}[BR]
  </#if>[BR]
</#list>[BR]
</ul>[BR]
<p>That's all.

剥离空白功能可以通过 ftl 指令 在模板中开启或关闭。如果你没有通过 ftl 指令来指定, 那么剥离空白功能是开启还是关闭就依据程序员是如何设置 FreeMarker 的了。 默认的情况下剥离空白是开启的,程序员可以留着不管(建议这样做)。请注意开启剥离空白时 不会 降低模板执行的效率,剥离空白的操作在模板加载时就已经完成了。

剥离空白可以为单独的一行关闭,就是使用nt 指令(对没有去掉空白的行来说)。

使用compress指令

另外一种方法就是使用compress 指令。和剥离空白相反,这个工作是直接基于生成的输出内容, 而不是对于模板进行。也就是说,它会动态地检查输出内容, 而不会检查生成输出FTL的程序。它会很强势地移除缩进, 空行和重复的空格/制表符(可以阅读参考手册 部分来获取更多信息)。所以对于下面这段代码:

<#compress>
<#assign users = [{"name":"Joe",        "hidden":false},
                  {"name":"James Bond", "hidden":true},
                  {"name":"Julia",      "hidden":false}]>
List of users:
<#list users as user>
  <#if !user.hidden>
  - ${user.name}
  </#if>
</#list>
That's all.
</#compress>

将会输出:

List of users:
- Joe
- Julia
That's all.

请注意 compress 是完全独立于剥离空白特性的。 所以它剥离模板中的空白是可能的,那么之后输出的内容就是被 压缩 过的。

此外,在默认情况下,名为 compress 的用户自定义指令是可以在数据模型中存在的(由于向下兼容特性)。 这和指令是相同的,除了可以选择设置 single_line 参数, 这将会移除所有的介于其中的换行符。在最后那个例子中,如果使用<@compress single_line=true>...</@compress> 来代替<#compress>...</#compress>, 那么就会得到如下输出:

List of users: - Joe - Julia That's all.