Go 视图模板篇(一):模板引擎的定义、解析与执行

共 3670字,需浏览 8分钟

 ·

2020-08-22 03:09

1、模板和模板引擎

在 Web 编程中,模板引擎用于聚合数据和模板并生成最终的 HTML 文档,处理器调用模板引擎来完成这一工作并将 HTML 文档作为响应实体发送给客户端:

虽然模板引擎没有统一的标准,甚至不同的模板引擎提供的功能特性也是天差地别,但是仍然可以划分为两种不同的类型:

  • 无业务逻辑:数据通过指定占位符替换,模板中不包含业务逻辑,所有业务逻辑都在处理器中完成,这样做的好处是将业务逻辑和数据渲染很好的隔离开。

  • 嵌入业务逻辑:在视图模板中嵌入业务逻辑,这使得视图模板的功能非常强大,但是这样一来,也使得代码维护非常困难。

我们倾向于无业务逻辑嵌入的模板引擎,这样的视图模板性能更好,可维护性更好,但是绝对的无业务逻辑嵌入也是做不到的(比如一些简单的条件判断和循环),大部分时候这取决于业务开发团队的约定,尽量不要在视图模板中编写业务逻辑代码。

PHP 诞生之初就是一个将业务逻辑和 HTML 视图混为一体的脚本语言,不过现在的 PHP 脚本中已经很少看到 HTML 代码了,这是 PHP 框架的功劳,比如 Laravel、Yii,PHP 自身作为一个模板引擎,现在已经衍生出独立的模板引擎,比如 Smarty、Blade。

实际上,大部分模板引擎都是介于以上两种类型之间,只能说离谁更近一些。

Go 语言官方提供的模板引擎 text/templatehtml/template 也是这样的混合物。

2、Go 模板引擎

Go 模板引擎都是在处理器中触发,指定要解析的模板文件,并传入待渲染的数据,最后返回由模板引擎最终生成的 HTML 作为 HTTP 响应发送给客户端:

Go 模板都是文本文档,在 Web 应用中,通常是 HTML 文档,其中包含了嵌入的命令。这些文档会被 Go 模板引擎解析和执行,生成另外的文本片段(替换完命令和数据)。

Go 标准库提供了 text/template 库用于解析任意类型的文本格式模板,以及 html/template 库用于解析并处理 HTML 格式模板。

在这些模板中,命令以 {{}} 包裹(实际上,这些界定符可以通过程序进行修改),下面我们看一段简单的模板代码 tmpl.html


<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Go Web Programmingtitle>
    head>
    <body>
        {{ . }}
    body>
html>

模板文件中的内容必须是可读的文本格式,但是扩展名可以随意,在这里由于最终生成的是 HTML 文档,所以我们使用了 .html 扩展名(tmpl.html)。

{{ . }} 中的 . 就是一个命令,用于在模板执行时替换从处理器传入的变量。

使用 Go 模板引擎通常包括以下两个步骤:

  • 解析文本模板源,可以是表单字符串、或者模板文件,用于创建解析后的模板结构体。

  • 执行解析后的模板,传递 ResponseWriter 和变量数据,这样一来,模板引擎就可以基于模板和数据生成最终的 HTML 并将其传递给 ResponseWriter 发送给客户端。

下面是服务端处理器调用模版引擎渲染上述模板代码的示例:

package main

import (
    "html/template"
    "net/http"
)

func process(w http.ResponseWriter, r *http.Request)  {
    t, _ := template.ParseFiles("tmpl.html"// 解析模板,返回 Template
    t.Execute(w, "Hello World!")  // 执行模板,并将其传递给 w
}

func main()  {
    http.HandleFunc("/template", process)
    http.ListenAndServe(":8080"nil)
}

运行上述代码启动服务器,在终端窗口通过 curl 请求 /template 路由,返回结果如下:

表明 HTML 模板解析渲染成功,对应的 {{ . }} 也别替换成 Hello World!

解析模板

在上面的示例代码中,我们调用了 ParseFiles 方法解析模板文件并创建稍后执行的解析后的 Template。其底层分为两步,它可以接收一个或多个模板文件名称,传入多个模板文件名的时候,会以第一个文件名作为模板名称,后续其它模板通常是第一个模板或者其他模板嵌套的子模板。

此外,我们还可以通过 ParseGlob 方法解析模板,该方法传入的参数是模式匹配串,而不是文件名称:

t, _ := template.ParseFiles("tmpl.html")
t, _ := template.ParseGlob("*.html")

如果当前路径只有一个 tmpl.html 模板文件,上述代码的效果是一样的。

除了解析文件之外,还支持解析字符串,实际上,所有解析方法最终调用的都是 Parse 方法:

package main

import (
    "html/template"
    "net/http"
)

func parseFiles(w http.ResponseWriter, r *http.Request)  {
    t, _ := template.ParseFiles("tmpl.html")
    t.Execute(w, "Hello World!")
}

func parseString(w http.ResponseWriter, r *http.Request)  {
    tmpl := 
        
            
            Go Web Programming
        
        
            {{ . }}
         
    `


    t := template.New("tmpl.html")
    t.Parse(tmpl)
    t.Execute(w, "Hello World!")
}

func main()  {
    http.HandleFunc("/template", parseFiles)
    http.HandleFunc("/string", parseFiles)
    http.ListenAndServe(":8080"nil)
}

上面 parseString 方法和 parseFiles 方法实现的效果是一样的,实际上  template.ParseFiles("tmpl.html") 底层调用的代码正是:

t := template.New(filename)
t.Parse(string// string 就是读取传入 file 的文本内容

在上面的代码中,我们忽略了 template.ParseFiles 返回的错误信息,不过,Go 官方建议我们对这个错误进行处理,为此,Go 还提供了更简洁的方式来处理模板解析过程中出现的错误:

t := template.Must(template.ParseFiles("tmpl.html"))

这种情况下,如果解析模板过程中出现问题,则抛出 panic(在 Go 语言中,panic 有点类似其它语言的异常,当函数内抛出 panic 时,会一直上溯到 main 入口,然后崩溃)。

执行模板

如果只解析一个模板文件的话,使用 Execute 方法就够了,如果要解析多个模板文件,也可以使用 Execute 方法,这个时候,会使用传入模板文件的第一个作为模板名称,并将其作为入口模板,如果要指定其它模板作为入口模板(或者称之为布局模板),需要调用 ExecuteTemplate 方法并将模板名作为第二个参数传递进去:

t, _ := template.ParseFiles("t1.html""t2.html")
t.Execute(w, "Hello World!")
t.ExecuteTemplate(w, "t1.html""Hello World!")

上面的 t.Executet.ExecuteTemplate 执行结果等效,如果你要从 t2.html 开始解析,需要这样指定入口文件:

t.ExecuteTemplate(w, "t2.html""Hello World!")


(全文完)



推荐阅读



学习交流 Go 语言,扫码回复「进群」即可


站长 polarisxu

自己的原创文章

不限于 Go 技术

职场和创业经验


Go语言中文网

每天为你

分享 Go 知识

Go爱好者值得关注



浏览 55
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报