前言

项目需求,需要将服务器的数据进行导出,方便携带展示,做了一次 html 报告的导出,考虑如何实现。

  1. 使用 vue 等前端框架,将数据导出成一个个的 json 文件,然后通过读取 js 来动态渲染 html 视图
  2. 直接将数据写入 html 页面,多导出一些 html,通过 iframe 来进行页面的加载

这里使用的是第二种方式。

本篇主要介绍一下 go 的一个开源库 hero 预编译模板引擎,用于快速的渲染 html

常见的模板引擎一般有两种实现方式,一种是直接解析 HTML 语法树,然后根据一定的规则动态的拼接,另外一种是把模板预先生成代码,渲染模板时调用相关的函数即可。

go 官方内置的 template 包是第一种实现方式,本篇的主角用的是第二种。

hero 简介

可以直接去 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" %>

    • 该语句把要include的模板加载进该模板,工作原理和C++中的#include有点类似。
    • 例: <%+ "user.html" >
  • 包导入语句 <%! go code %>

    • 该语句用来声明所有在函数外的代码,包括依赖包导入、全局变量、const等。

    • 该语句不会被子模板所继承

    • 例:

      <%!import ("fmt""strings")var a intconst 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, 2c := Add(a, b)
      %>
      
  • 原生值语句 <%==[t] variable %>

    • 该语句把变量转换为string。

    • t是变量的类型,hero会自动根据t来选择转换函数。t的待选值有:

      • b: bool
      • i: int, int8, int16, int32, int64
      • u: byte, uint, uint8, uint16, uint32, uint64
      • f: float32, float64
      • s: string
      • bs: []byte
      • v: 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 %>

    • 该语句注释相关模板,注释不会被生成到go代码里边去。
    • 例: <# 这是一个注释 >.

原理

最终生成的代码,就是通过字符串拼接,写入 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 页面的布局和数据的插入对应。

参考

https://github.com/shiyanhui/hero

go 导出 html 报告(使用 hero 预编译 html 模板引擎)相关推荐

  1. Jquery 模板插件 jquery.tmpl.js 的使用方法(2):嵌套each循环,temp调用(使用预编译的模板缓存)...

    直接上代码吧 一:主窗口 /*#region SendChooseTargetTemplate 发送候选人主窗口模板*/ var SendChooseTargetTemplate = ''; Send ...

  2. C#中的预编译指令介绍

    原文:C#中的预编译指令介绍 1.#define和#undef 用法: #define DEBUG #undef DEBUG #define告诉编译器,我定义了一个DEBUG的一个符号,他类似一个变量 ...

  3. 课程 预编译框架,开发高性能应用 - 微软技术暨生态大会 2018

    微软技术暨生态大会(Tech Summit),2018 年在上海世博中心召开.这是最后一次的 Tech Summit 了:明年开始,中国大陆地区就要和其他国家和地区一样,进行全球 Ignite Tou ...

  4. RT-Thread中如何预编译一个.c文件

    本文介绍在RT-Thread系统,使用scons,如何预编译一个.c文件 首先新建一个测试文件test.c #include "rtthread.h"void test(void) ...

  5. 使用预编译库PREBUILT LIBRARY官方说明

    使用预编译库 NDK 支持使用预编译库(同时支持静态库和共享库).此功能有以下两个主要用例: 向第三方 NDK 开发者分发您自己的库(而不分发您的源代码). 使用您自己的库的预编译版本来提升编译速度. ...

  6. android应用资源预编译,编译和打包全解析

    我们知道,在一个APK文件中,除了有代码文件之外,还有很多资源文件.这些资源文件是通过Android资源打包工具aapt(Android Asset Package Tool)打包到APK文件里面的. ...

  7. 从预编译的角度理解Swift与Objective-C及混编机制

    本文从预编译的基础知识入手,由浅至深的介绍了 Objective-C 和 Swift 的工作机制,并通过这些机制来解释混编项目中使用到的技术和各种参数的作用,由此来指导开发者如何进行混编. 写在前面 ...

  8. 浅谈GCC预编译头技术

    浅谈GCC预编译头技术 文/jorge --谨以此文,悼念我等待MinGW编译时逝去的那些时间. 其 实刚开始编程的时候,我是丝毫不重视编译速度之类的问题的,原因很简单,因为那时我用BASICA.后来 ...

  9. PHP 预编译加速: eAccelerator的安装和性能比较

    eAccelerator已经是很常用的PHP平台预编译加速的手段了.今天在自己机器上尝试安装了一下,备忘如下: 获得源代码: http://bart.eaccelerator.net/source/ ...

  10. 5单个编译总会编译全部_5分钟读懂JavaScript预编译

    大家都知道JavaScript是解释型语言,既然是解释型语言,就是编译一行,执行一行,那又何来预编译一说呢?脚本执行js引擎都做了什么呢?今天我们就来看看吧. 1-JavaScript运行三部曲 语法 ...

最新文章

  1. linux selinux 安全子系统简介
  2. PHP上传图片到数据库和存储到本地文件夹的方法
  3. Android布局管理器-使用FrameLayout帧布局管理器显示层叠的正方形以及前景照片
  4. Ajax(jquery)
  5. 利用shell脚本监控网站状态
  6. MySQL多种安装方式选择
  7. 在windows 2008 R2上安装sharepoint 2013时遇到提示必须安装 .netframeword4.5的处理办法...
  8. 重新创建Activity
  9. pat乙级相当于什么水平_雅思6分是什么水平?相当于英语几级
  10. *nix下部署第三方动态库文件
  11. @Autowired 与@Resource的区别
  12. 假如我来发明编程语言
  13. CentOS 7.4创建普通用户赋予登录权限
  14. 邹城的关于机器人教育_【喜报】我校机器人队问鼎全国大学生机器人大赛冠军!...
  15. Android 开发环境搭建之——ADT-Bundle for Windows
  16. python正交表运用
  17. Mac m1 安装jdk
  18. Bitvise密钥登录Linux服务器
  19. char在mysql中的意思_mysql中char表示什么意思
  20. 阿里巴巴 Excel工具easyExcel

热门文章

  1. eclipse基础环境搭建(含Tomcat、maven)
  2. AP类WiFi模块系列二:半成品主板式大功率AP类WiFi模块
  3. VB2010(29)Web部署应用程序
  4. C#编程VS开发工具安装Nupkg包
  5. Linux学习笔记之CentOS7的 wheel组
  6. 机器学习中分类和聚类的区别
  7. 复合线转权属线lisp_三权发证中使用MAPGIS CASS如何应用复合线 简单、快速生成集体土地所有权权属线 -...
  8. 下载Bootstrap
  9. java面试官如何面试别人
  10. UIFont各种字体