命名空间

目录

当运行 FTL 模板时,可能会使用使用 assign 和 macro 指令创建的变量,这些变量将放在一个集合下,这个集合被称为命名空间。 简单的情况下可以只使用一个命名空间,称之为主命名空间,因为通常只使用该命名空间,所以就没有意识到这点。如下:

<#assign username="hxstrive">
<p>名称:${username}</p>

上面实例中,username 存在当前模版的主命名空间中。

使用引用库,你可以创建可以重复使用的宏,函数和其他变量的集合。如果考虑在一些项目中和他人共享使用的时候,你可能会有一个很大的宏集合。但要确保库中没有宏名称和数据模型中变量名称同名,而且也不能和模板中引用其他库中的变量名称同名是不可能的。 通常来说,变量因为名称冲突是相互冲突(会导致两则均不能用)。 所以要为每个库中的变量使用不同的命名空间,这就使得使用多个命名空间是必然的。

创建一个库

我们来建立一个简单的库。假设你需要通用的变量 copyright 和 mail:

<#macro copyright date>
  <p>Copyright (C) ${date} Julia Smith. All rights reserved.</p>
</#macro>
<#assign mail = "jsmith@acme.com">

把上面的这些定义存储在文件 “libs/namespace.ftl” 中。

如果在 demo14.ftl 中使用 <#include "libs/namespace.ftl">,那么就会在主命名空间中创建两个变量,这样就不是很好,因为可能会与主命名空间中其他变量冲突。如下:

<#-- 引用库,库中的变量将写入到主命名空间 -->
<#include "libs/namespace.ftl">
<@copyright date="2020-06-28"></@copyright>
<p>mail:${mail}</p>

如果我们只想让 copyright 和 mail 变量位于 “myNamespace” 命名空间中,所以就不得不使用 import 指令来代替 include 指令。

乍一看,import 指令和 include 很相似,但是 import 为 libs/namespace.ftl 创建一个空的命名空间,然后将 copyright 和 mail 变量放到 空的命名空间。

如果 demo14.ftl 想访问 copyright 和 mail 这两个变量,而 demo14.ftl 使用的是主命名空间,就不能看到其他命名空间中的变量。解决方法是 import 指令不仅仅创建命名空间,而且要通过 import 的调用者创建一个新的哈希表变量,这就成为进入新的命名空间的大门。那么 demo14.ftl 就像下面这样:

<#-- 引用库,使用 import 指令,创建 myNamespace 命名空间 -->
<#import "libs/namespace.ftl" as myNamespace>
<@myNamespace.copyright date="2020-06-28" />
<p>mail:${myNamespace.mail}</p>

要注意它是怎么访问为 libs/namespace.ftl 创建的命名空间中的变量的,使用新创建的命名空间访问哈希表 myNamespace。将会输出:

<p>Copyright (C) 2020-06-28 Julia Smith. All rights reserved.</p>
<p>mail:jsmith@acme.com</p>

如果在主命名空间中有一个变量,名为 mail 或 copyright,那么就不会引起混乱了, 因为两个模板使用了不同的命名空间。例如,在 libs/namespace.ftl 中修改 copyright 成如下这样:

<!-- libs/namespace2.ftl -->
<#macro copyright date>
    <p>Copyright (C) ${date} Julia Smith. All rights reserved.</p>
    <p>Email: ${mail}</p>
</#macro>
<#assign mail = "jsmith@acme.com">

然后替换 demo14.ftl 中的内容:

<#-- 引用库,使用 import 指令,创建 myNamespace 命名空间 -->
<#import "libs/namespace2.ftl" as myNamespace>
<#assign mail="hxstrive@outlook.com">
<@myNamespace.copyright date="2020-06-28"/>
${myNamespace.mail}
${mail}

将会输出:

<p>Copyright (C) 2020-06-28 Julia Smith. All rights reserved.</p>
jsmith@acme.com
hxstrive@outlook.com

当调用了 copyright 宏之后,输出和上面的是相似的, 因为 FreeMarker 已经暂时转向由 import 指令为 libs/namespace2.ftl 生成的命名空间了。因此, copyright 宏看到这里存在的变量 mail, 而不是主命名空间中存在的其它 mail。

如果在  libs/namespace2.ftl 中没有使用 assign 声明 mail 变量,将抛出如下错误信息:

FreeMarker template error:
The following has evaluated to null or missing:
==> mail  [in template "libs/namespace2.ftl" at line 3, column 17]
----
Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${mail}  [in template "libs/namespace2.ftl" in macro "copyright" at line 3, column 15]

这是因为在 libs/namespace2.ftl 中,使用了没有声明的 mail 变量,mail 只在主命名空间进行了声明。

更新命名空间中的变量

偶尔想要在一个被包含的命名空间上创建或替换一个变量。 那么可以使用 assign 指令, 如果用到了它的 namespace 变量,例如下面这样:

<#-- 在引入的命名空间中编写变量 -->
<#import "libs/namespace2.ftl" as myNamespace2>
<p>${myNamespace2.mail}</p>
<#assign mail="hxstrive@outlook.com" in myNamespace2>
<p>${myNamespace2.mail}</p>
<#assign url="https://www.hxstrive.com" in myNamespace2>
<p>${myNamespace2.url}</p>

将会输出:

<p>jsmith@acme.com</p>
<p>hxstrive@outlook.com</p>
<p>https://www.hxstrive.com</p>

命名空间和数据模型

数据模型中的变量在任何位置都是可见的。例如,如果在数据模型中有一个名为 user 的变量,那么 lib/namespace2.ftl 也能访问它, demo14.ftl 当然也能:

<#macro copyright date>
  <p>Copyright (C) ${date} ${user}. All rights reserved.</p>
</#macro>
<#assign mail = "${user}@acme.com">

如果 user 是 “hxstrive” 的话,下面这个例子:

<#-- 引用库,使用 import 指令,创建 myNamespace 命名空间 -->
<#import "libs/namespace3.ftl" as myNamespace>
<@myNamespace.copyright date="2020-06-28"/>
${myNamespace.mail}

将会输出:

<p>Copyright (C) 2020-06-28 hxstrive. All rights reserved.</p>
hxstrive@outlook.com

不要忘了在模板的命名空间中的变量有着比数据模型中的变量更高的优先级。因此,数据模型的内容不会干涉到由库创建的变量。

【注意】

在一些特殊的应用中,也许想在模板中创建所有命名空间都可见的变量,就像数据模型中的变量一样。但是你不能在模板中改变数据模型,可以通过 global 指令来达到相似的效果。

命名空间的生命周期

命名空间由使用 import 指令中所写的路径来识别。如果想多次 import 这个路径,那么只会为第一次 import 引用创建命名空间并执行模板。后面相同路径的 import 只是创建一个哈希表当作访问相同命名空间的 “门”。 例如,在 demo14.ftl 中:

<#import "libs/namespace3.ftl" as my>
<#import "libs/namespace3.ftl" as foo>
<#import "libs/namespace3.ftl" as bar>
${my.mail}, ${foo.mail}, ${bar.mail}
<#assign mail="jsmith@other.com" in my>
${my.mail}, ${foo.mail}, ${bar.mail}

将会输出:

jsmith@acme.com, jsmith@acme.com, jsmith@acme.com
jsmith@other.com, jsmith@other.com, jsmith@other.com

这里可以看到通过 my, foo 和 bar 访问相同的命名空间。

【注意】

命名空间是不分层次的,它们相互之间是独立存在的。 那么,如果在命名空间 N1 中 import 命名空间 N2, 那 N2 也不在 N1 中,N1 只是可以通过哈希表来访问 N2。这和在主命名空间中 import N2,然后直接访问命名空间 N2 是一样的过程。

每一次模板的执行过程,它都有一个私有的命名空间的集合。每一次模板执行工作都是一个分离且有序的过程, 它们仅仅存在一段很短的时间,同时页面用以渲染内容, 然后就和所有填充过的命名空间一起消失了。因此,无论何时我们说第一次调用 import,一个单一模板执行工作的内容都是这样。

为他人编写库

如果你已经为其他人员编写一个有用的,高质量的库,你也许想把它放在网络上。 为了防止和其他作者使用库的命名相冲突,而且引入其他库时要书写简单, 这有一个事实上的标准,那就是指定库路径的格式。这个标准是: 库的路径必须对模板和其他库可用,就像这样:

/lib/yourcompany.com/your_library.ftl

如果你为 Example 公司工作,它们拥有 www.example.com 网的主页, 你的工作是开发一个部件库,那么要引入你所写的FTL的路径应该是:

/lib/example.com/widget.ftl

请注意,www已经被省略了。第三次路径分割后的部分可以包含子目录,可以像下面这样写:

/lib/example.com/commons/string.ftl

一个重要的规则就是路径不应该包含大写字母,为了分隔词语, 使用下划线 _,就像 wml_form (而不是 wmlForm )。

请注意,如果你的工作不是为公司或组织开发库,你应该使用项目主页的URL,比如 /lib/example.sourceforge.net/example.ftl,或 /lib/geocities.com/jsmith/example.ftl。

说说我的看法
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号