🌟 一、核心概念

FreeMarker 提供了两种用于模板复用的核心指令:

指令 作用 特点
<#include> 包含另一个模板的内容(直接插入) 类似静态包含,不创建新命名空间
<#import> 导入另一个模板并创建一个新的命名空间 类似函数库导入,支持调用宏、变量等

1. <#include> 的作用

  • 将指定模板的内容直接插入到当前位置。
  • 插入的内容与当前模板共享变量作用域。
  • 适用于包含静态内容、布局片段、公共头部/尾部等。

2. <#import> 的作用

  • 将指定模板作为一个模块导入。
  • 创建一个新的命名空间,防止变量冲突。
  • 可以调用导入模板中定义的宏、变量等。
  • 适用于构建可复用的组件库、宏库、工具模板。

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

✅ 步骤 1:使用 <#include> 包含模板

示例 1:基本用法

<#include "header.ftl">

说明:将 header.ftl 模板内容插入当前位置。

示例 2:传递参数(FreeMarker 2.3.25+)

<#include "greeting.ftl" with name="张三">

greeting.ftl 内容:

<p>你好,${name}!</p>

✅ 步骤 2:使用 <#import> 导入模板

示例 1:导入并调用宏

<#import "macros.ftl" as m>

<@m.renderButton text="提交" color="blue" />

macros.ftl 内容:

<#macro renderButton text color>
  <button style="color:${color}">${text}</button>
</#macro>

示例 2:导入多个宏或变量

<#import "utils.ftl" as utils>

<p>当前年份:${utils.currentYear}</p>

utils.ftl 内容:

<#assign currentYear = .now?string("yyyy")>

✅ 步骤 3:动态导入/包含模板

<#assign templateName = "footer_" + lang + ".ftl">
<#include templateName>

说明:根据 lang 的值动态加载不同语言的 footer 模板。


✅ 步骤 4:使用 <#attempt> 处理可选模板

<#attempt>
  <#include "optionalContent.ftl">
<#recover>
  <p>可选内容未找到,已忽略</p>
</#recover>

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

❌ 错误 1:模板路径错误

<#include "wrongPath/header.ftl">  <#-- 报错:模板未找到 -->

解决方法:

  • 检查模板路径是否正确(相对路径或绝对路径)
  • 确保模板位于配置的 TemplateLoader 路径下

❌ 错误 2:导入宏时未使用命名空间

<#import "macros.ftl" as m>
<@renderButton />  <#-- 报错:未找到宏 -->

解决方法: 使用命名空间调用宏 <@m.renderButton />


❌ 错误 3:在 <#import> 模板中定义变量未使用 <#assign>

<#import "utils.ftl" as utils>
${utils.userName}  <#-- 报错:变量未定义 -->

utils.ftl 内容:

userName = "张三"  <#-- 错误写法 -->

正确写法:

<#assign userName = "张三">

❌ 错误 4:宏未定义或调用方式错误

<@m.undefinedMacro />  <#-- 报错:宏未定义 -->

解决方法:

  • 确保宏在导入模板中已定义
  • 使用 <#macro> 正确声明宏

❌ 错误 5:模板循环包含

<!-- a.ftl -->
<#include "b.ftl">

<!-- b.ftl -->
<#include "a.ftl">

后果: 无限递归导致栈溢出或模板渲染失败。

解决方法: 避免循环包含。


🧠 四、使用技巧

1. 使用 <#import> 构建组件库

<#import "components/buttons.ftl" as btn>
<@btn.primary text="提交" />

2. 使用 <#include> 实现模板继承(类似布局)

<!-- layout.ftl -->
<html>
<head><title>${title}</title></head>
<body>
  <#include "header.ftl">
  ${content}
  <#include "footer.ftl">
</body>
</html>

<!-- page.ftl -->
<#assign title = "首页" content = "这是首页内容">
<#include "layout.ftl">

3. 使用 <#include> 实现动态内容切换

<#assign page = "home">
<#include "${page}.ftl">

4. 使用 <#import> 实现工具函数库

<#import "utils.ftl" as utils>
<p>当前时间:${utils.currentTime}</p>

utils.ftl:

<#assign currentTime = .now?string("yyyy-MM-dd HH:mm:ss")>

🏆 五、最佳实践

实践项 建议
使用 <#import> 实现组件化开发 将可复用的宏、变量封装为模块
使用 <#include> 实现布局和静态内容 如 header、footer、导航栏等
模板路径统一管理 避免硬编码路径,提高可维护性
避免循环包含 防止无限递归导致崩溃
使用 <#attempt> 包裹可选模板 提高容错性
统一命名空间前缀 m, utils, components 等,提高可读性
避免在 <#import> 模板中直接输出内容 只定义宏或变量,不直接输出 HTML

🔧 六、性能优化

优化项 说明
避免频繁导入相同模板 FreeMarker 会缓存导入的模板,但应避免重复导入
减少 <#include> 嵌套层级 过多嵌套影响渲染性能
避免在 <#list> 中频繁调用 <#import> 应提前导入并缓存宏或变量
合理使用 <#include><#import> <#import> 创建命名空间,性能略低于 <#include>
模板缓存机制 FreeMarker 默认缓存模板,确保模板加载效率
避免大模板频繁导入 可拆分模块,按需导入

📚 七、总结

维度 内容
核心指令 <#include>, <#import>
适用场景 模板复用、组件化开发、布局管理
调用方式 <#include "path">, <#import "path" as name>
注意事项 路径问题、命名空间、避免循环包含
最佳实践 组件库、布局继承、工具模块、错误处理
性能优化 减少嵌套、避免重复导入、合理使用缓存

📌 附录:常见用法示例汇总

场景 示例
包含公共头部 <#include "header.ftl">
导入宏库 <#import "macros.ftl" as m>
调用宏 <@m.renderButton text="提交" />
动态包含模板 <#include "${page}.ftl">
传递参数包含 <#include "greeting.ftl" with name="张三">
安全包含 <#attempt> ... <#recover> ... </#recover></#attempt>
定义工具变量 <#assign currentTime = .now?string("yyyy-MM-dd")>

📚 推荐阅读


📌 总结一句话:
<#include><#import> 是 FreeMarker 中实现模板复用的核心机制。<#include> 适用于静态内容插入,<#import> 适用于模块化开发与组件化复用。掌握其使用方式、注意事项与最佳实践,是构建可维护、高性能模板系统的关键。