在 FreeMarker 模板引擎中,<#nested>
是 <#macro>
的一个强大特性,用于在宏定义中插入调用者提供的内容。它使得宏不仅可以接收参数,还可以接收嵌套的 HTML 或文本内容,从而实现布局模板、组件封装、条件包裹等高级功能。
本文将从 核心概念、操作步骤、常见错误、注意事项、使用技巧、最佳实践 与 性能优化 等多个维度,全面讲解 FreeMarker 中 <#nested>
的使用,帮助你快速掌握关键知识并实践。
一、核心概念
概念 | 描述 |
---|---|
<#nested> |
宏中用于插入调用者提供内容的占位符 |
<#macro> |
定义宏时,可以包含 <#nested> 作为内容占位符 |
调用方式 | 使用 <@macroName>...</@macroName> 包裹内容进行调用 |
嵌套层级 | 支持多层嵌套,但建议控制层级以提高可读性 |
参数传递 | 可结合 <#nested> 使用宏参数,实现动态内容包裹 |
二、操作步骤(详细)
1. 定义包含 <#nested>
的宏
<#macro box>
<div class="box">
<#nested>
</div>
</#macro>
2. 调用宏并传入内容
<@box>
<p>This is the content inside the box.</p>
</@box>
输出结果:
<div class="box">
<p>This is the content inside the box.</p>
</div>
2. 定义带标题和内容的宏
<#macro box title>
<div class="box">
<h3>${title}</h3>
<div class="content">
<#nested>
</div>
</div>
</#macro>
调用方式:
<@box title="User Info">
<p>Name: Alice</p>
<p>Age: 25</p>
</@box>
3. 嵌套多个 <#nested>
(FreeMarker 2.3.29+)
<#macro layout header footer>
<html>
<head><title>Page</title></head>
<body>
<header>
<#nested "header">
</header>
<main>
<#nested>
</main>
<footer>
<#nested "footer">
</footer>
</body>
</html>
</#macro>
调用方式:
<@layout>
<#nested header>
<h1>Header Content</h1>
</#nested>
<p>Main content here.</p>
<#nested footer>
<p>Footer Content</p>
</#nested>
</@layout>
4. <#nested>
与 <#local>
结合使用
<#macro greet name>
<#local greeting = "Hi">
<#nested>
<p>${greeting}, ${name}!</p>
</#macro>
调用方式:
<@greet name="Alice">
<p>Welcome to our site.</p>
</@greet>
三、常见错误与注意事项
1. <#nested>
未被包裹在 <#macro>
中
<#nested> <#-- 错误:不在宏中 -->
✅ 解决方法:确保 <#nested>
只在宏定义中使用。
2. <#nested>
调用时未提供内容
<@box /> <#-- 无内容,<#nested> 不会渲染 -->
✅ 建议:使用 <#if>
判断是否有内容再渲染 <#nested>
。
3. <#nested>
嵌套标签未正确闭合
<@box title="Title"> <#-- 缺少闭合标签 -->
✅ 解决方法:必须使用 <@box title="Title">...</@box>
包裹内容。
4. <#nested>
参数名称不匹配
<#macro layout>
<#nested "header">
</#macro>
<@layout>
<#nested "footer"> <#-- 错误:没有定义 footer 区域 -->
</@layout>
✅ 解决方法:宏中定义的 <#nested>
参数必须与调用时一致。
四、使用技巧
1. 使用 <#nested>
实现页面布局(布局模板)
<#macro layout title>
<html>
<head><title>${title}</title></head>
<body>
<#nested>
</body>
</html>
</#macro>
调用方式:
<@layout title="Home">
<h1>Welcome</h1>
<p>This is the home page.</p>
</@layout>
2. 使用 <#nested>
实现组件包裹(如卡片、按钮)
<#macro card title>
<div class="card">
<h3>${title}</h3>
<div class="content">
<#nested>
</div>
</div>
</#macro>
调用方式:
<@card title="User Info">
<p>Name: Alice</p>
<p>Age: 25</p>
</@card>
3. 使用 <#nested>
封装条件逻辑
<#macro ifNotEmpty list>
<#if list?has_content>
<#nested>
</#if>
</#macro>
调用方式:
<@ifNotEmpty list=users>
<ul>
<#list users as user>
<li>${user.name}</li>
</#list>
</ul>
</@ifNotEmpty>
4. 使用 <#nested>
实现多区域布局
<#macro layout header footer>
<html>
<head><title>Page</title></head>
<body>
<header><#nested "header"></header>
<main><#nested></main>
<footer><#nested "footer"></footer>
</body>
</html>
</#macro>
调用方式:
<@layout>
<#nested header><h1>Header</h1></#nested>
<p>Main content</p>
<#nested footer><p>Footer</p></#nested>
</@layout>
五、最佳实践
实践 | 说明 |
---|---|
✅ 使用 <#nested> 实现布局模板 |
如页面结构、盒子结构等 |
✅ 使用 <#nested> 封装 UI 组件 |
如卡片、按钮、表格行等 |
✅ 使用 <#nested> 实现条件包裹 |
如 ifNotEmpty 、ifLoggedIn 等 |
✅ 使用 <#local> 避免变量污染 |
保证宏内部变量不干扰外部作用域 |
✅ 宏名统一命名规范 | 如 layout.main 、component.card 等 |
✅ 控制嵌套层级 | 避免嵌套过深影响可读性 |
六、性能优化
优化点 | 说明 |
---|---|
✅ 避免 <#nested> 中频繁调用方法 |
方法调用会降低性能,建议预处理后传入 |
✅ 启用模板缓存 | FreeMarker 支持模板缓存,避免重复解析 |
✅ 合理使用 <#nested> 参数 |
避免过多区域定义,影响渲染效率 |
✅ 减少 <#nested> 嵌套层级 |
多层嵌套会增加渲染复杂度 |
七、总结
概念 | 说明 | 推荐使用场景 |
---|---|---|
<#nested> |
宏中插入调用者内容 | 布局模板、UI 组件、逻辑封装 |
<#nested "name"> |
多区域嵌套内容 | 多区域布局、条件分块 |
<#macro> |
定义宏模板 | 模块化开发、内容复用 |
<#local> |
宏内局部变量 | 避免变量污染 |
<#nested> + <#if> |
条件包裹内容 | 如 ifNotEmpty 、ifLoggedIn |
嵌套宏 | 宏中调用其他宏 | 提高模板模块化程度 |
八、参考示例代码(Java + FTL)
Java 示例数据
Map<String, Object> model = new HashMap<>();
List<Map<String, Object>> users = new ArrayList<>();
Map<String, Object> user = new HashMap<>();
user.put("name", "Alice");
user.put("age", 25);
users.add(user);
model.put("users", users);
FTL 模板示例
<h2><#nested> 示例</h2>
<#macro card title>
<div class="card">
<h3>${title}</h3>
<div class="content">
<#nested>
</div>
</div>
</#macro>
<#macro layout title>
<html>
<head><title>${title}</title></head>
<body>
<#nested>
</body>
</html>
</#macro>
<#-- 使用 card 宏 -->
<@card title="User Info">
<p>Name: Alice</p>
<p>Age: 25</p>
</@card>
<#-- 使用 layout 宏 -->
<@layout title="User Page">
<h2>User List</h2>
<#list users as user>
<@card title="User Info">
<p>Name: ${user.name}</p>
<p>Age: ${user.age}</p>
</@card>
</#list>
</@layout>
九、结语
掌握 FreeMarker 中 <#nested>
的使用,是构建高效、模块化模板系统的关键。通过 <#nested>
,你可以实现布局模板、组件封装、条件包裹等高级功能,极大提升模板的可读性、可维护性和复用性。
✅ 最终建议:
- 所有需要包裹内容的宏都应使用
<#nested>
; - 使用
<#nested>
实现布局和组件封装; - 使用
<#local>
避免宏变量污染; - 控制
<#nested>
嵌套层级; - 启用模板缓存提升性能;
- 宏名统一命名规范,避免冲突。