最近在使用 Velocity 模板语法进行邮件的开发。Velocity 是一个基于 Java 的模板引擎,主要用于生成 HTML、邮件等文本输出。它提供了一种简单而强大的模板语言(VTL,Velocity Template Language),可以用来在 HTML 页面中引用 Java 代码定义的对象,实现界面与代码的分离。在这里介绍一下基本的使用。
基础语法:
- 变量引用:使用
$符号,如$user.name - 设置变量:使用
#set指令,如#set($name = "value") - 条件判断:使用
#if、#else、#elseif指令 - 循环遍历:使用
#foreach指令 - 注释:使用
##单行注释
调试
可以直接利用 velocity.js 这个项目在node中进行调试,在空文件下执行下面的命令初始化项目
$ npm init -y
$ npm install velocityjs
安装完成之后,编写test.js文件,用于测试
const Velocity = require('velocityjs');
const fs = require('fs');
// 读取模板文件
const template = fs.readFileSync('./example.vm', 'utf-8');
//准备mock数据
const context = {}
// 渲染模板
const result = Velocity.render(template, context);
// 输出结果
fs.writeFileSync('output.html', result);
在example.vm文件中写入文件之后,终端中执行
$ node test.js
就可以在output.html文件中看到输出了
实战:表格单元格合并
我们以一个单元格合并为例,具体说明 Velocity 的使用。这个示例将展示如何合并具有相同年龄值的单元格。
进行项目初始化之后,首先在example.vm文件中创建一个简单的表格
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<table border="1">
<tr>
<th>姓名</th>
<th>年龄</th>
</tr>
#foreach($item in $tableList)
<tr>
<td>$item.name</td>
<td>$item.age</td>
</tr>
#end
</table>
</body>
</html>
创建之后编写对应的test.js文件,同时给context赋值
const context = {
tableList: [
{
name: '张三',
age: 20,
},
{
name: '李四',
age: 25,
},
{
name: '王五',
age: 25,
},
],
};
在终端中执行$ node test.js之后,就可以打开output.html文件看到输出了
接下来我们尝试将相同的年龄进行合并,通常来说,单元格合并可以封装成一个通用的方法,然后根据传入的列进行操作,但是Velocity语法本身功能不算很丰富,实现起来需要做很多的前期准备,对于我们处理的邮件这种小型的功能,实现起来得不偿失,所以考虑直接实现功能比较划算。
合并单元格的原理都是一致的,基本上都是遍历一遍数据,记录相同的行的数量,然后给rowspan赋上该数量,同时被合并掉的单元格不进行渲染即可。
首先使用#set定义好需要的变量
#set($currentAge = '') ## 用于记录当前遍历到哪一项
#set($ageCount = 0) ## 计数器,用于记录相同的行数
#set($outCounter = 0) ## 记录外侧foreach索引
#set($insideCounter = 0) ## 记录内侧foreach索引
然后进行双重循环,遍历拿到相同的行的数量即可
#foreach($item in $tableList)
#set($outCounter = $outCounter + 1) ## 初始时就设置为第二行的数据,便于与第一行比较
<tr>
<td>$item.name</td>
## 当前年龄项与上一个年龄项不同,或者是第一次比较时时,需要进行新的单元格处理。
#if($currentAge != $item.age || $outCounter == 1)
#set($currentAge = $item.age) ## 更新当前年龄值
#set($ageCount = 1) ## 重置计数器
#set($insideCounter = $outCounter) ## 设置内层循环的起始位置,这样保证只会向下比较
#foreach($next in $tableList) ## 向下继续查找相同年龄的行数
## 确保不会超出表格数据范围,将当前年龄与下一行的年龄进行比较
#if($insideCounter < $tableList.size() && $currentAge == $tableList.get($insideCounter).age)
#set($ageCount = $ageCount + 1) ## 计数器 + 1
#set($insideCounter = $insideCounter + 1) ## 移动到下一行继续比较
#else
#break
#end
#end
## 只会渲染有计数的行
<td class="cell-left" colspan="3" #if($ageCount > 1)rowspan="$ageCount"#end>$!item.age</td>
#end
</tr>
#end