在 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 模板,提升开发效率与模板质量。