Preface
According to project requirements, the server data needs to be exported to facilitate portability and display. I have exported a html
report and considered how to implement it.
- Use
vue
other front-end frameworks to export data into files one by onejson
, and thenjs
dynamically renderhtml
the view by reading - Write the data directly to
html
the page, export morehtml
, andiframe
load the page through
The second method is used here.
This article mainly introduces go
an open source library hero precompiled template engine for fast rendering html
.
Common template engines generally have two implementation methods. One is to directly parse the HTML syntax tree and then dynamically splice it according to certain rules. The other is to pre-generate code for the template and call the relevant functions when rendering the template.
go
The official built-in template
package is the first implementation method, and the protagonist of this article uses the second method.
hero introduction
You can go directly to github to see it. Here is a brief introduction.
Install
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
use
hero [options]
options:
- source: 模板目录,默认为当前目录
- dest: 生成的go代码的目录,如果没有设置的话,和source一样
- pkgname: 生成的go代码包的名称,默认为template
- extensions: source文件的后缀, 如果有多个则用英文逗号隔开, 默认为.html
- watch: 是否监控模板文件改动并自动编译
example:
hero -source="./"
hero -source="$GOPATH/src/app/template" -watch
basic grammar
Hero
There are nine types of statements in total, they are:
-
function definition statement
<%: func define %>
- This statement defines the function corresponding to the template. If there is no function definition statement in a template, the corresponding function will not be generated in the final result.
- The last parameter of the function must be
*bytes.Buffer
orio.Writer
, hero will automatically recognize the name of the parameter and write the result to the parameter. - example:
<%: func UserList(userList []string, buffer *bytes.Buffer) %>
<%: func UserList(userList []string, w io.Writer) %>
<%: func UserList(userList []string, w io.Writer) (int, error) %>
-
template inheritance statement
<%~ "parent template" %>
- This statement declares the template to be inherited.
- example:
<%~ "index.html" >
-
Template include statement
<%+ "sub template" %>
- This statement loads the template to be included into the template. The working principle is somewhat similar
C++
to that in#include
. - example:
<%+ "user.html" >
- This statement loads the template to be included into the template. The working principle is somewhat similar
-
package import statement
<%! go code %>
-
This statement is used to declare all code outside the function, including dependent package imports, global variables, const, etc.
-
This statement will not be inherited by child templates
-
example:
<%! 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 } %>
-
-
block statement
<%@ blockName { %> <% } %>
-
The block statement is used to rewrite the block of the same name in the parent model in the child template, thereby realizing template inheritance.
-
example:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <%@ body { %> <% } %> </body> </html>
-
-
Go code statements
<% go code %>
-
This statement defines the portion of code inside the function.
-
example:
<% for _, user := range userList { %> <% if user != "Alice" { %> <%= user %> <% } %> <% } %> <% a, b := 1, 2 c := Add(a, b) %>
-
-
native value statement
<%==[t] variable %>
-
This statement converts the variable to string.
-
t
is the type of the variable, hero will automaticallyt
select the conversion function based on it.t
The candidate values are:b
: booli
: int, int8, int16, int32, int64u
: byte, uint, uint8, uint16, uint32, uint64f
: float32, float64s
: stringbs
: []bytev
: interface
Notice:
- If
t
not set,t
the default iss
. - It is best not to use it
v
because the corresponding conversion function isfmt.Sprintf("%v", variable)
, which is very slow.
-
example:
<%== "hello" %> <%==i 34 %> <%==u Add(a, b) %> <%==s user.Name %>
-
-
escape value statement
<%= statement %>
-
This statement converts the variable into a string and then
html.EscapesString
escapes it through memory. -
t
Same as in the native value statement abovet
. -
example:
<%= a %> <%= a + b %> <%= Add(a, b) %> <%= user.Name %>
-
-
comment statement
<%# note %>
- This statement annotates the relevant template, and the annotation will not be generated into the go code.
- Example:
<# 这是一个注释 >
.
principle
The final generated code is written through string concatenation io.Writer
. Here is an example, the generated code is as follows:
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())
}
Summarize
Use go
Generate html
, the principle is very simple, just write the data into the corresponding place through string splicing. The troublesome point is that html
the layout of the page corresponds to the insertion of data.