一、核心概念

在 FreeMarker 模板引擎中,<#function> 用于在模板中定义可复用的函数,类似于编程语言中的方法或函数。通过 <#function> 定义的函数可以在模板中多次调用,提高模板的可维护性和复用性。

1. <#function> 的作用

  • 定义可复用的逻辑片段。
  • 接收参数,执行计算或逻辑处理。
  • 可以返回值(通过 <#return>)。
  • 作用域为当前模板,不能跨模板调用。

2. <#function><#macro> 的区别

特性 <#function> <#macro>
是否返回值 ✅ 是 ❌ 否(仅输出内容)
返回方式 <#return value> 直接输出内容
使用场景 逻辑计算、返回结果 生成 HTML 或文本片段
调用方式 ${functionName(args)} <@macroName args... />

🛠️ 二、操作步骤(详细)

✅ 步骤 1:定义函数

使用 <#function> 定义一个函数,语法如下:

<#function functionName param1 param2 ...>
  <#-- 函数体 -->
  <#return 返回值>
</#function>

示例:定义一个加法函数

<#function add a b>
  <#local sum = a + b>
  <#return sum>
</#function>

✅ 步骤 2:调用函数

使用 ${functionName(args...)} 调用函数,并获取返回值。

${add(3, 5)}  <#-- 输出:8 -->

✅ 步骤 3:函数参数传递

函数可以接收任意数量的参数,支持以下类型:

  • 数字
  • 字符串
  • 布尔值
  • 序列(列表)
  • 哈希(Map)
  • 函数(高阶函数)

示例:多参数函数

<#function formatUser name age>
  <#return "姓名:${name},年龄:${age}">
</#function>

${formatUser("张三", 25)}  <#-- 输出:姓名:张三,年龄:25 -->

✅ 步骤 4:使用局部变量(<#local>

函数中推荐使用 <#local> 定义变量,避免污染全局作用域。

<#function multiply a b>
  <#local product = a * b>
  <#return product>
</#function>

✅ 步骤 5:嵌套函数调用

函数可以调用其他函数。

<#function square x>
  <#return multiply(x, x)>
</#function>

${square(4)}  <#-- 输出:16 -->

✅ 步骤 6:处理默认参数(模拟)

FreeMarker 不支持默认参数语法,但可以通过 <#if> 判断模拟。

<#function greet name>
  <#if name??>
    <#return "你好,${name}!">
  <#else>
    <#return "你好,陌生人!">
  </#if>
</#function>

${greet("李四")}  <#-- 输出:你好,李四! -->
${greet()}       <#-- 输出:你好,陌生人! -->

✅ 步骤 7:函数返回复杂结构(Map、List)

函数可以返回复杂结构,如 Map 或 List。

<#function getUserInfo id name age>
  <#return {"id": id, "name": name, "age": age}>
</#function>

<#assign user = getUserInfo(1, "王五", 30)>
用户ID:${user.id}<br>
用户名:${user.name}<br>
年龄:${user.age}

⚠️ 三、常见错误与注意事项

❌ 错误 1:未使用 <#return> 返回值

<#function noReturn x>
  <#local result = x * 2>
</#function>

${noReturn(5)}  <#-- 报错:函数没有返回值 -->

解决方法: 每个函数必须有 <#return>


❌ 错误 2:函数名重复

<#function add a b>
  <#return a + b>
</#function>

<#function add x y>  <#-- 报错:函数名重复 -->
  <#return x + y>
</#function>

解决方法: 函数名在模板中必须唯一。


❌ 错误 3:在 <#function> 外使用 <#return>

<#function badReturn x>
</#function>
<#return x>  <#-- 报错:不在函数体内 -->

解决方法: <#return> 只能在 <#function> 块中使用。


❌ 错误 4:函数参数未传递

<#function requiredParam x>
  <#return x * 2>
</#function>

${requiredParam()}  <#-- 报错:参数缺失 -->

解决方法: 调用时必须传递所有参数,或在函数中判断是否为空。


🧠 四、使用技巧

1. 使用函数封装业务逻辑

<#function isAdult age>
  <#return age >= 18>
</#function>

<#if isAdult(20)>
  <p>已成年</p>
<#else>
  <p>未成年</p>
</#if>

2. 使用函数生成 HTML 片段

<#function renderButton text color>
  <#return "<button style='color:${color}'>${text}</button>">
</#function>

${renderButton("提交", "blue")}

3. 函数返回模板片段(结合 <#assign>

<#function getGreeting name>
  <#return "欢迎 ${name} 访问我们的网站!">
</#function>

<#assign greeting = getGreeting("张三")>
<p>${greeting}</p>

4. 函数返回布尔值用于条件判断

<#function isEven x>
  <#return (x % 2) == 0>
</#function>

<#if isEven(4)>
  <p>是偶数</p>
<#else>
  <p>是奇数</p>
</#if>

🏆 五、最佳实践

实践项 建议
函数命名清晰 使用动词或名词描述函数功能,如 formatPrice, calculateTotal
避免副作用 函数应只返回结果,不修改全局变量
保持函数单一职责 一个函数只做一件事
使用 <#local> 定义变量 避免污染全局作用域
合理使用返回值类型 返回值应为数字、字符串、Map、List 等结构化数据
封装复杂逻辑 将复杂判断或计算封装为函数,提高可读性
函数复用优先 重复逻辑应提取为函数,避免模板冗余

🔧 六、性能优化

优化项 说明
减少函数嵌套 多层嵌套影响性能和可读性
避免频繁调用复杂函数 将结果缓存到变量中,避免重复计算
避免在循环中频繁调用函数 提前计算并赋值给变量
函数体尽量简洁 函数体过大影响性能,建议拆分
减少字符串拼接函数 多次拼接影响性能,建议使用 <#assign><#macro> 替代

📚 七、总结

维度 内容
核心指令 <#function>, <#return>
函数特点 可返回值、可接收参数、不可跨模板调用
调用方式 ${functionName(args...)}
返回值 支持数字、字符串、Map、List 等
注意事项 必须有 <#return>、函数名唯一、不可在函数外使用 <#return>
最佳实践 单一职责、命名清晰、封装逻辑、使用 <#local>
性能优化 减少嵌套、缓存结果、避免重复调用

📌 附录:函数调用示例汇总

函数定义 调用方式 返回值
<#function add a b><#return a + b></#function> ${add(2, 3)} 5
<#function greet name><#return "你好," + name + "!"></#function> ${greet("李四")} 你好,李四!
<#function isEven x><#return (x % 2) == 0></#function> ${isEven(4)} true
<#function getUser id name><#return {"id": id, "name": name}></#function> <#assign u = getUser(1, "王五")> {"id":1,"name":"王五"}

📚 推荐阅读


📌 总结一句话:
<#function> 是 FreeMarker 中用于封装可复用逻辑的核心机制,通过合理定义与调用函数,可以显著提升模板的可维护性与复用性。但应遵循最佳实践,避免副作用和性能瓶颈,使模板逻辑清晰、高效。