项目需求,需要将服务器的数据进行导出,方便携带展示,做了一次 html
报告的导出,考虑如何实现。
vue
等前端框架,将数据导出成一个个的 json
文件,然后通过读取 js
来动态渲染 html
视图html
页面,多导出一些 html
,通过 iframe
来进行页面的加载这里使用的是第二种方式。
本篇主要介绍一下 go
的一个开源库 hero 预编译模板引擎,用于快速的渲染 html
。
常见的模板引擎一般有两种实现方式,一种是直接解析 HTML 语法树,然后根据一定的规则动态的拼接,另外一种是把模板预先生成代码,渲染模板时调用相关的函数即可。
go
官方内置的 template
包是第一种实现方式,本篇的主角用的是第二种。
可以直接去 github 上看,这里做一个简短的介绍。
go get github.com/shiyanhui/hero
go get github.com/shiyanhui/hero/hero
// Hero需要goimports处理生成的go代码,所以需要安装goimports.
go get golang.org/x/tools/cmd/goimports
hero [options]
options:
- source: 模板目录,默认为当前目录
- dest: 生成的go代码的目录,如果没有设置的话,和source一样
- pkgname: 生成的go代码包的名称,默认为template
- extensions: source文件的后缀, 如果有多个则用英文逗号隔开, 默认为.html
- watch: 是否监控模板文件改动并自动编译
example:
hero -source="./"
hero -source="$GOPATH/src/app/template" -watch
Hero
总共有九种语句,他们分别是:
函数定义语句 <%: func define %>
*bytes.Buffer
或者io.Writer
, hero会自动识别该参数的名字,并把把结果写到该参数里。<%: func UserList(userList []string, buffer *bytes.Buffer) %>
<%: func UserList(userList []string, w io.Writer) %>
<%: func UserList(userList []string, w io.Writer) (int, error) %>
模板继承语句 <%~ "parent template" %>
<%~ "index.html" >
模板include语句 <%+ "sub template" %>
C++
中的#include
有点类似。<%+ "user.html" >
包导入语句 <%! go code %>
该语句用来声明所有在函数外的代码,包括依赖包导入、全局变量、const等。
该语句不会被子模板所继承
例:
<%!
import (
"fmt"
"strings"
)
var a int
const b = "hello, world"
func Add(a, b int) int {
return a + b
}
type S struct {
Name string
}
func (s S) String() string {
return s.Name
}
%>
块语句 <%@ blockName { %> <% } %>
块语句是用来在子模板中重写父模中的同名块,进而实现模板的继承。
例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<%@ body { %>
<% } %>
</body>
</html>
Go代码语句 <% go code %>
该语句定义了函数内部的代码部分。
例:
<% for _, user := range userList { %>
<% if user != "Alice" { %>
<%= user %>
<% } %>
<% } %>
<%
a, b := 1, 2
c := Add(a, b)
%>
原生值语句 <%==[t] variable %>
该语句把变量转换为string。
t
是变量的类型,hero会自动根据t
来选择转换函数。t
的待选值有:
b
: booli
: int, int8, int16, int32, int64u
: byte, uint, uint8, uint16, uint32, uint64f
: float32, float64s
: stringbs
: []bytev
: interface注意:
t
没有设置,那么t
默认为s
.v
,因为其对应的转换函数为fmt.Sprintf("%v", variable)
,该函数很慢。例:
<%== "hello" %>
<%==i 34 %>
<%==u Add(a, b) %>
<%==s user.Name %>
转义值语句 <%= statement %>
该语句把变量转换为string后,又通过html.EscapesString
记性转义。
t
跟上面原生值语句中的t
一样。
例:
<%= a %>
<%= a + b %>
<%= Add(a, b) %>
<%= user.Name %>
注释语句 <%# note %>
<# 这是一个注释 >
.最终生成的代码,就是通过字符串拼接,写入 io.Writer
。下面是一个例子,生成后的代码如下:
func WriteTreeNodeHtml(param *RenderTemplateParam, w io.Writer) {
_buffer := hero.GetBuffer()
defer hero.PutBuffer(_buffer)
_buffer.WriteString(`
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="css/build.css" />
<link rel="stylesheet" href="css/jquery.treeview.css" />
<link rel="stylesheet" href="css/screen.css" />
<script src="js/jquery.min.js"></script>
<script src="js/jquery.cookie.js"></script>
<script src="js/jquery.treeview.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
$("#tree").treeview({
collapsed: true,
animated: "fast",
control: "#sidetreecontrol",
prerendered: true,
persist: "location"
});
})
</script>
</head>
<body style="margin: 10px;">
<div>
<h3>`)
hero.EscapeHTML(GetAppName(), _buffer)
_buffer.WriteString(`报告</h3>
<div id=jstree style="font-size:14px">
<ul class="treeview" id="tree" style="margin-top:6px;">
<li><a class="jstree-anchor" href="page1.html#case" target="pageframe">
<i style="margin-left: 4px;margin-right: 4px;" class="icon-file iconfont"></i>案件</a></li>
<li><a class="jstree-anchor" href="page1.html#evidences" target="pageframe">
<i style="margin-left: 4px;margin-right: 4px;" class="icon-evidence iconfont"></i>检材信息</a></li>
<li><a class="jstree-anchor" href="page1.html#brief" target="pageframe">
<i style="margin-left: 4px;margin-right: 4px;" class="icon-evidence iconfont"></i>数据统计文字概括</a></li>
<li><a class="jstree-anchor" href="page1.html#summary" target="pageframe">
<i style="margin-left: 4px;margin-right: 4px;" class="icon-summary iconfont"></i>数据统计清单</a></li>
`)
treeNodes, ok := param.Data.([]*ReportTreeNode)
if !ok {
return
}
for _, node := range treeNodes {
GenerateTreeNode(node, _buffer)
}
_buffer.WriteString(`
</ul>
</div>
</div>
</body>
</html>
`)
w.Write(_buffer.Bytes())
}
使用 go
生成 html
,原理很简单,通过字符串拼接,将数据都写入对应的地方即可。麻烦的点就在于 html
页面的布局和数据的插入对应。