🌟 一、核心概念
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>
适用于模块化开发与组件化复用。掌握其使用方式、注意事项与最佳实践,是构建可维护、高性能模板系统的关键。