1.模板继承

用于统一继承导航栏、页脚等公有部分,通过block、define、template实现模板继承。

base.tmpl

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
    .head{height: 50px; background-color: red;width: 100%;text-align: center;}
    .main{width: 100%;}
    .main .left{width: 30%;height: 1000px;float: left;background-color:violet;text-align: center;}
    .main .right{width: 70%;float: left;text-align: center;height: 1000px;background-color:yellowgreen;}
</style>
</head>
<body>
    <div class="head">
        <h1>head</h1>
    </div> 
    <div class="main">
        <div class="left">
        <h1>side</h1>
        </div>
        <div class="right">
            {{ block "content" . }}
            <h1>content</h1>
            {{ end }}
        </div>
    </div>
</body>
</html>

index.tmpl

{{ template "base.tmpl" . }}
{{ define "content" }}
<h5>这是index页面</h5>
{{ . }}
{{ end }}

home.tmpl

{{ template "base.tmpl" . }}
{{ define "content" }}
<h5>这是home页面</h5>
{{ . }}
{{ end }}

main.go

package main
import (
    "html/template"
    "net/http"
)
func indexHandleFunc(w http.ResponseWriter, r *http.Request) {
    t := template.New("index.tmpl")
    t, _ = t.ParseFiles("./base.tmpl", "./index.tmpl")
    t.Execute(w, "index")
}
func homeHandleFunc(w http.ResponseWriter, r *http.Request) {
    t := template.New("home.tmpl")
    t, _ = t.ParseFiles("./base.tmpl", "./home.tmpl")
    t.Execute(w, "home")
}
func main() {
    server := http.Server{
        Addr: "localhost:8080",
    }
    http.HandleFunc("/index", indexHandleFunc)
    http.HandleFunc("/home", homeHandleFunc)
    server.ListenAndServe()
}

2.修改默认的标识符

Go标准库的模板引擎使用的花括号{{和}}作为标识,而许多前端框架(如Vue和 AngularJS)也使用{{和}}作为标识符,所以当同时使用Go语言模板引擎和以上前端框架时就会出现冲突,需要修改标识符,修改前端的或者修改Go语言的。这里演示如何修改Go语言模板引擎默认的标识符:

template.New("test").Delims("{[", "]}").ParseFiles("./t.tmpl")

3.html/template的上下文感知

对于html/template包,有一个很好用的功能:上下文感知。text/template没有该功能。上下文感知具体指的是根据所处环境css、js、html、url的path、url的query,自动进行不同格式的转义。

3.1index.tmpl

<!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>
    <div>{{ . }}</div>
</body>
</html>

4.2main.go

package main
import (
    // "text/template"
    "html/template"
    "net/http"
)
func indexHandleFunc(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("./index.tmpl")
    data := `<script>alert("helloworld")</script>`
    t.Execute(w, data)
}
func main() {
    http.HandleFunc("/", indexHandleFunc)
    http.ListenAndServe(":8080", nil)
}

运行程序,页面显示:<script>alert("helloworld")</script>

默认是不转义

转义,即编写自定义函数输出javaScript内容

不想转义的话,可以自行编写一个safe函数,手动返回一个template.HTML类型的内容

index.tmpl

<!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>
    <div>{{ .str1 }}</div>
    <div>{{ .str2 | safe }}</div>
</body>
</html>

main.go

package main
import (
    // "text/template"
    "html/template"
    "net/http"
)
func indexHandleFunc(w http.ResponseWriter, r *http.Request) {
    t := template.New("index.tmpl")
    t.Funcs(template.FuncMap{
        "safe": func(str string) template.HTML {
            return template.HTML(str)
        },
    }).ParseFiles("./index.tmpl")
    m := map[string]interface{}{
        "str1": `<script>alert("helloworld")</script>`,
        "str2": `<a href = "http://baidu.com">baidu</a>`,
    }
    t.Execute(w, m)
}

4.go:embed打包静态资源

4.1未用go:embed打包

package main
import (
    "log"
    "net/http"
)
func main() {
    mux := http.DefaultServeMux
    mux.Handle("/web/js/", http.StripPrefix("/web/js/", http.FileServer(http.Dir("static/js/"))))
    mux.Handle("/web/css/", http.StripPrefix("/web/css/", http.FileServer(http.Dir("static/css/"))))
    mux.Handle("/web/img/", http.StripPrefix("/web/img/", http.FileServer(http.Dir("static/img/"))))
    log.Fatal(http.ListenAndServe(":8080", mux))
}

说明:static/js表示物理目录,浏览器访问http://localhost:8080/web/js/test.js即可访问到对应文件。

4.2用go:embed

Project structure
go.mod
main.go
static/css/main.css
templates/index.html.tmpl
title.txt
main.go
package main
import (
	"embed"
	"html/template"
	"log"
	"net/http"
)
// The go embed directive statement must be outside of function body
// Embed the file content as string.
//go:embed title.txt
var title string

// Embed the entire directory.
//go:embed templates
var indexHTML embed.FS

//go:embed static
var staticFiles embed.FS

func main() {
	// Note the call to ParseFS instead of Parse
	t, err := template.ParseFS(indexHTML, "templates/index.html.tmpl")
	if err != nil {
		log.Fatal(err)
	}

	// http.FS can be used to create a http Filesystem
	var staticFS = http.FS(staticFiles)
	fs := http.FileServer(staticFS)

	// Serve static files
	http.Handle("/static/", fs)
	// Handle all other requests
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		var path = req.URL.Path
		log.Println("Serving request for path", path)
		w.Header().Add("Content-Type", "text/html")

		// respond with the output of template execution
		t.Execute(w, struct {
			Title    string
			Response string
		}{Title: title, Response: path})

	})

	log.Println("Listening on :3000...")
	// start the server
	err = http.ListenAndServe(":3000", nil)
	if err != nil {
		log.Fatal(err)
	}
}
index.html.tmpl
<html>
<head>
<title>{{.Title}}</title>
<link rel="stylesheet" href="static/css/main.css">
</head>
<body>
<p>{{.Response}}</p>
<p>Powered by // go:embed</p>
</body>
</html>
title.txt
This is the title.