HTML模版引擎输出内容如何格式化
在Web开发中,HTML模版引擎是连接后端数据与前端展示的重要工具。很多时候我们从数据库或接口获取的数据并不符合直接展示的需求,比如日期格式不统一、数字需要添加千分位分隔符、文本需要截断显示等,这时候就需要在模版引擎层面完成内容格式化,让前端页面直接拿到可读性强的内容。
常见的内容格式化场景
实际开发中,模版引擎需要处理的格式化需求主要分为以下几类:
- 日期时间格式化:将时间戳或标准日期字符串转换为用户所在时区的指定格式,比如把
2024-05-20T08:30:00Z转换为2024年05月20日 16:30 - 数字格式化:给整数添加千分位分隔符,保留指定小数位数,或者将大数字转换为易读的缩写形式,比如把
1234567转换为123.46万 - 文本格式化:对过长文本进行截断并添加省略号,转义HTML特殊字符防止XSS攻击,或者对敏感信息进行脱敏处理
- 枚举值映射:将后端返回的枚举编码转换为对应的中文描述,比如把
0转换为待审核,1转换为已通过
以Nunjucks为例实现内容格式化
Nunjucks是JavaScript生态中常用的模版引擎,支持自定义过滤器来扩展格式化能力。下面我们通过代码示例展示如何实现几种常见的格式化逻辑。
1. 注册自定义过滤器
首先需要在初始化Nunjucks环境时注册自定义的格式化过滤器,这些过滤器可以在模版中直接调用,对变量进行处理。
const nunjucks = require('nunjucks');
// 初始化模版引擎环境
const env = nunjucks.configure('views', {
autoescape: true, // 默认开启HTML转义,防止XSS
noCache: true
});
// 注册日期格式化过滤器
env.addFilter('dateFormat', function(dateStr, format = 'YYYY-MM-DD HH:mm:ss') {
const date = new Date(dateStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
});
// 注册千分位数字格式化过滤器
env.addFilter('numberFormat', function(num, decimals = 2) {
if (isNaN(num)) return '0';
const number = Number(num);
// 保留指定小数位
const fixedNum = number.toFixed(decimals);
// 拆分整数和小数部分
const [integerPart, decimalPart] = fixedNum.split('.');
// 给整数部分添加千分位分隔符
const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
});
// 注册文本截断过滤器
env.addFilter('textTruncate', function(text, maxLength = 20, suffix = '...') {
if (!text) return '';
if (text.length <= maxLength) return text;
return text.slice(0, maxLength) + suffix;
});
// 注册枚举映射过滤器
const statusMap = {
0: '待审核',
1: '已通过',
2: '已驳回',
3: '已过期'
};
env.addFilter('statusText', function(statusCode) {
return statusMap[statusCode] || '未知状态';
});2. 在模版中使用格式化过滤器
注册完过滤器后,就可以在HTML模版中通过管道符|调用对应的过滤器,对输出内容进行处理。注意模版中的<div>、<span>等标签需要正常书写,但提到标签名称时如果是描述性内容需要转义,这里在模版中使用的标签是实际执行的,不需要转义。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>内容格式化示例</title>
</head>
<body>
<div class="order-list">
<h2>订单列表</h2>
{% for order in orderList %}
<div class="order-item">
<p>订单编号:{{ order.id }}</p>
<p>下单时间:{{ order.createTime | dateFormat('YYYY年MM月DD日 HH:mm') }}</p>
<p>订单金额:{{ order.amount | numberFormat(2) }} 元</p>
<p>订单状态:{{ order.status | statusText }}</p>
<p>商品描述:{{ order.goodsDesc | textTruncate(15) }}</p>
</div>
{% endfor %}
</div>
</body>
</html>3. 后端传递数据并渲染
最后在后端逻辑中准备好需要渲染的数据,调用模版引擎的渲染方法即可输出格式化后的内容。
// 模拟后端获取的订单数据
const orderList = [
{
id: 'ORD20240520001',
createTime: '2024-05-20T09:15:00Z',
amount: 1234.567,
status: 1,
goodsDesc: '这是一款非常实用的无线蓝牙耳机,续航时间长,音质清晰'
},
{
id: 'ORD20240520002',
createTime: '2024-05-21T14:22:00Z',
amount: 9876543.21,
status: 0,
goodsDesc: '轻薄便携的笔记本电脑,适合日常办公和轻度娱乐使用'
}
];
// 渲染模版并返回给前端
const html = env.render('order-list.html', { orderList });
console.log(html);其他模版引擎的格式化实现思路
不同的模版引擎实现格式化的核心思路是类似的,都是提供自定义函数扩展的能力:
- EJS:可以在渲染时传递格式化函数作为上下文变量,在模版中直接调用函数处理内容,比如
<%= formatDate(order.createTime) %> - Vue/React的模版:可以在组件中定义格式化方法,在模版绑定中调用方法,比如
{{ formatDate(order.createTime) }} - Java体系的Thymeleaf:可以通过自定义方言或者工具类的方式,在模版中使用
${#dates.format(order.createTime, 'yyyy-MM-dd')}这样的语法完成格式化
注意事项
在进行内容格式化时,有几个要点需要特别注意:
- 优先在模版引擎层处理格式化,而不是在前端JS中处理,减少前端不必要的计算,同时避免数据格式不一致的问题
- 涉及用户输入的内容,一定要开启HTML转义,防止XSS攻击,比如Nunjucks默认的autoescape选项就是开启状态
- 过滤器要尽量保持纯函数特性,不要依赖外部可变状态,方便复用和测试
- 如果格式化逻辑比较复杂,比如需要调用第三方接口获取映射关系,建议放在后端业务逻辑层处理,模版层只做简单的展示性格式化