在 FreeMarker 模板引擎中,变量定义 是控制模板逻辑和数据展示的关键手段。<#assign><#local><#global> 是 FreeMarker 提供的三种变量定义指令,分别用于定义局部变量当前命名空间变量全局变量。本文将从 核心概念操作步骤常见错误注意事项使用技巧最佳实践性能优化 等多个维度,全面讲解这三种变量定义方式,帮助你快速掌握并实践。


一、核心概念

指令 作用域 可见性 是否可覆盖
<#assign> 当前命名空间 当前模板和子宏可见
<#local> 本地作用域 仅当前宏或当前 <#macro> 内部可见
<#global> 全局作用域 所有模板、宏、函数都可见

1. <#assign>

  • 作用:定义当前命名空间中的变量。
  • 适用范围:模板中任意位置,宏外部或宏内部。
  • 可被覆盖:是。

2. <#local>

  • 作用:定义宏内部的局部变量。
  • 适用范围:只能在 <#macro> 定义的宏内部使用。
  • 可被覆盖:否(仅限当前宏内)。

3. <#global>

  • 作用:定义全局变量,在所有模板中可见。
  • 适用范围:任意位置。
  • 可被覆盖:是(但需小心,避免冲突)。

二、操作步骤(详细)

1. <#assign> 定义变量

<#assign name = "Alice">
<p>Hello, ${name}</p>

示例输出:

Hello, Alice

2. <#assign> 定义带表达式的变量

<#assign age = 25>
<#assign isAdult = age >= 18>
<p>Is adult: ${isAdult?string("Yes", "No")}</p>

3. <#assign> 定义嵌套结构(Map)

<#assign user = {"name": "Bob", "age": 30}>
<p>Name: ${user.name}, Age: ${user.age}</p>

4. <#assign> 定义列表(List)

<#assign fruits = ["Apple", "Banana", "Orange"]>
<#list fruits as fruit>
  <p>${fruit}</p>
</#list>

5. <#assign> 与宏结合使用

<#macro greet name>
  <#assign message = "Hello, ${name}">
  ${message}
</#macro>

${greet("Alice")}

6. <#local> 定义局部变量(只能在宏内使用)

<#macro greet name>
  <#local greeting = "Hi">
  ${greeting}, ${name}
</#macro>

${greet("Bob")}

⚠️ 注意greeting 变量只能在宏内部访问,外部访问会抛出异常。

7. <#global> 定义全局变量

<#global siteName = "MySite">

在其他模板中:

<p>Site: ${siteName}</p>

三、常见错误与注意事项

1. <#local> 在宏外部使用导致异常

<#local name = "Alice">  <#-- 错误 -->

解决方法:确保 <#local> 只在 <#macro> 内部使用。


2. <#assign> 覆盖模板传入变量

<#-- 假设模型中传入了 user.name -->
<#assign name = "Guest">
<p>${name}</p>  <#-- 输出 Guest -->

建议:避免覆盖传入变量名,或使用默认值机制。

<#assign name = user.name!"Guest">

3. <#global> 变量名冲突

<#global name = "Alice">
<#global name = "Bob">  <#-- 会覆盖前面的值 -->

建议:全局变量命名应具有唯一性或前缀,如 global.siteName


4. <#assign><#local> 同名变量冲突

<#macro test>
  <#local name = "Local">
  <#assign name = "Assign">
  ${name}  <#-- 输出 Assign -->
</#macro>

说明<#assign> 会覆盖 <#local> 的同名变量。


四、使用技巧

1. 使用 <#assign> 缓存常用表达式结果

<#assign fullName = user.firstName + " " + user.lastName>
<p>Full Name: ${fullName}</p>

2. 使用 <#assign> 模拟“if-else”逻辑

<#assign role = user.role!"Guest">
<#assign welcome = role == "Admin" ? "Welcome Admin" : "Welcome User">
<p>${welcome}</p>

3. 使用 <#assign> 定义常量

<#assign MAX_ITEMS = 10>
<#if items?size > MAX_ITEMS>
  <p>Too many items.</p>
</#if>

4. 使用 <#global> 实现跨模板共享数据

<#global config = {"theme": "dark", "version": "1.0"}>

在其他模板中:

Theme: ${config.theme}

5. 使用 <#local> 避免变量污染

<#macro formatList items>
  <#local count = 0>
  <#list items as item>
    <#assign count = count + 1>
    <p>${count}. ${item}</p>
  </#list>
</#macro>

五、最佳实践

实践 说明
✅ 优先使用 <#assign> 适用于大多数变量定义场景,灵活且作用域合理。
✅ 在宏中使用 <#local> 避免宏内部变量污染外部作用域。
✅ 慎用 <#global> 避免全局变量污染,建议加前缀如 global.config
✅ 使用 <#assign> 缓存计算结果 提高模板执行效率,减少重复计算。
✅ 避免变量名重复 尤其是 <#assign><#local> 之间,避免覆盖问题。
✅ 使用默认值机制 防止模板变量未定义导致异常,如 ${name!"Guest"}

六、性能优化

优化点 说明
✅ 避免在循环中频繁定义变量 使用 <#assign> 在循环外缓存结果。
✅ 使用 <#assign> 替代复杂表达式 减少模板中重复计算。
✅ 避免滥用 <#global> 全局变量查找效率较低,建议仅用于共享配置。
✅ 启用模板缓存 提高模板加载和渲染性能。

七、总结

指令 作用域 可见性 是否可覆盖 推荐使用场景
<#assign> 当前命名空间 当前模板和宏可见 普通变量定义、缓存中间结果
<#local> 本地作用域 仅当前宏可见 宏内部变量,避免污染外部作用域
<#global> 全局作用域 所有模板可见 跨模板共享数据(如配置、主题)

八、参考示例代码(Java + FTL)

Java 示例数据

Map<String, Object> model = new HashMap<>();

// 用户信息
Map<String, String> user = new HashMap<>();
user.put("name", "Alice");
model.put("user", user);

// 列表数据
List<String> items = Arrays.asList("Item1", "Item2", "Item3");
model.put("items", items);

FTL 模板示例

<h2>变量定义示例</h2>

<#-- 使用 <#assign> -->
<#assign siteName = "MySite">
<#assign welcome = "Welcome to " + siteName>
<p>${welcome}</p>

<#-- 使用 <#local> -->
<#macro showUser name>
  <#local prefix = "User: ">
  ${prefix}${name}
</#macro>
<p>${showUser("Bob")}</p>

<#-- 使用 <#global> -->
<#global config = {"theme": "light", "version": "v1.0"}>
<p>Theme: ${config.theme}</p>

<#-- 缓存表达式 -->
<#assign fullName = user.name + " Smith">
<p>Full Name: ${fullName}</p>

<#-- 控制逻辑 -->
<#assign count = items?size>
<#if count > 2>
  <p>More than 2 items.</p>
</#if>

九、结语

掌握 FreeMarker 中 <#assign><#local><#global> 的使用,是构建高效、可维护模板系统的关键。通过合理使用这些变量定义指令,可以有效控制变量作用域、避免命名冲突、提升模板可读性和执行效率。

最终建议

  • 优先使用 <#assign> 定义变量;
  • 在宏中使用 <#local> 避免变量污染;
  • 慎用 <#global>,建议加前缀;
  • 使用默认值机制防止变量未定义;
  • 避免在循环中重复定义变量;
  • 启用模板缓存提高性能。

通过这些实践技巧,你可以更高效地编写 FreeMarker 模板,提升开发效率与模板质量。