gowebprograming

模板引擎

使用模板

hello.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>模板文件</title>
</head>
<body>
    <!-- 嵌入动作 -->
    
</body>
</html>

main.go

package main
import (
    "fmt"
    "html/template"
    "net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
	// t := template.New("hello.html")
	// t, err := t.ParseFiles("hello.html")
	// t := template.Must(template.ParseFiles("hello.html"))
    t, err := template.ParseFiles("./hello.html")
    if err != nil {
        fmt.Println(err)
        w.WriteHeader(500)
    }
    t.Execute(w, "Hello World!")
}
func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

常用方法

html.template.Template

// Template is a specialized Template from "text/template" that produces a safe
// HTML document fragment.
type Template struct {
    // Sticky error if escaping fails, or escapeOK if succeeded.
    escapeErr error
    // We could embed the text/template field, but it's safer not to because
    // we need to keep our version of the name space and the underlying
    // template's in sync.
    text *template.Template
    // The underlying template's parse tree, updated to be HTML-safe.
    Tree       *parse.Tree
    *nameSpace // common to all associated templates
}
// Execute applies a parsed template to the specified data object,
// writing the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
// A template may be executed safely in parallel, although if parallel
// executions share a Writer the output may be interleaved.
func (t *Template) Execute(wr io.Writer, data interface{}) error {
    if err := t.escape(); err != nil {
        return err
    }
    return t.text.Execute(wr, data)
}
// Parse parses text as a template body for t.
// Named template definitions ( or  statements) in text
// define additional templates associated with t and are removed from the
// definition of t itself.
//
// Templates can be redefined in successive calls to Parse,
// before the first use of Execute on t or any associated template.
// A template definition with a body containing only white space and comments
// is considered empty and will not replace an existing template's body.
// This allows using Parse to add new named template definitions without
// overwriting the main template body.
func (t *Template) Parse(text string) (*Template, error) {
    if err := t.checkCanParse(); err != nil {
        return nil, err
    }
    ret, err := t.text.Parse(text)
    if err != nil {
        return nil, err
    }
    // In general, all the named templates might have changed underfoot.
    // Regardless, some new ones may have been defined.
    // The template.Template set has been updated; update ours.
    t.nameSpace.mu.Lock()
    defer t.nameSpace.mu.Unlock()
    for _, v := range ret.Templates() {
        name := v.Name()
        tmpl := t.set[name]
        if tmpl == nil {
            tmpl = t.new(name)
        }
        tmpl.text = v
        tmpl.Tree = v.Tree
    }
    return t, nil
}
// New allocates a new HTML template associated with the given one
// and with the same delimiters. The association, which is transitive,
// allows one template to invoke another with a  action.
//
// If a template with the given name already exists, the new HTML template
// will replace it. The existing template will be reset and disassociated with
// t.
func (t *Template) New(name string) *Template {
    t.nameSpace.mu.Lock()
    defer t.nameSpace.mu.Unlock()
    return t.new(name)
}
// Must is a helper that wraps a call to a function returning (*Template, error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as
//  var t = template.Must(template.New("name").Parse("html"))
func Must(t *Template, err error) *Template {
    if err != nil {
        panic(err)
    }
    return t
}
// ParseFiles creates a new Template and parses the template definitions from
// the named files. The returned template's name will have the (base) name and
// (parsed) contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
//
// When parsing multiple files with the same name in different directories,
// the last one mentioned will be the one that results.
// For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
// named "foo", while "a/foo" is unavailable.
func ParseFiles(filenames ...string) (*Template, error) {
    return parseFiles(nil, filenames...)
}
// ParseFiles parses the named files and associates the resulting templates with
// t. If an error occurs, parsing stops and the returned template is nil;
// otherwise it is t. There must be at least one file.
//
// When parsing multiple files with the same name in different directories,
// the last one mentioned will be the one that results.
//
// ParseFiles returns an error if t or any associated template has already been executed.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
    return parseFiles(t, filenames...)
}
// ParseGlob creates a new Template and parses the template definitions from
// the files identified by the pattern. The files are matched according to the
// semantics of filepath.Match, and the pattern must match at least one file.
// The returned template will have the (base) name and (parsed) contents of the
// first file matched by the pattern. ParseGlob is equivalent to calling
// ParseFiles with the list of files matched by the pattern.
//
// When parsing multiple files with the same name in different directories,
// the last one mentioned will be the one that results.
func ParseGlob(pattern string) (*Template, error) {
    return parseGlob(nil, pattern)
}

解析模板

执行模板

// Template is a specialized Template from "text/template" that produces a safe
// HTML document fragment.
type Template struct {
    // Sticky error if escaping fails, or escapeOK if succeeded.
    escapeErr error
    // We could embed the text/template field, but it's safer not to because
    // we need to keep our version of the name space and the underlying
    // template's in sync.
    text *template.Template
    // The underlying template's parse tree, updated to be HTML-safe.
    Tree       *parse.Tree
    *nameSpace // common to all associated templates
}
// Execute applies a parsed template to the specified data object,
// writing the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
// A template may be executed safely in parallel, although if parallel
// executions share a Writer the output may be interleaved.
func (t *Template) Execute(wr io.Writer, data interface{}) error {
    if err := t.escape(); err != nil {
        return err
    }
    return t.text.Execute(wr, data)
}
// ExecuteTemplate applies the template associated with t that has the given
// name to the specified data object and writes the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
// A template may be executed safely in parallel, although if parallel
// executions share a Writer the output may be interleaved.
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
    tmpl, err := t.lookupAndEscapeTemplate(name)
    if err != nil {
        return err
    }
    return tmpl.text.Execute(wr, data)
}

Execute方法将解析好的模板应用到data上,并输出写入wr

动作

模板的动作是嵌入到模板的命令
:代表传递给模板的数据

<!-- 定义模板 -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
</body>
</html>

	在一个模板文件中定义多个模板
./Index.html
<!-- 定义模板 -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
</body>
</html>


<a href="#">点我有惊喜</a>
./main.go
package main
import (
    "fmt"
    "html/template"
    "net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
    t, err := template.ParseFiles("./index.html")
    if err != nil {
        fmt.Println(err)
        w.WriteHeader(500)
    }
    t.ExecuteTemplate(w, "model", "")
}
func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
} ```

在不同模板文件中定义同名的模板

./hello.html
<!-- 定义模板 -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>模板文件</title>
</head>
<body>
    
</body>
</html>

./content1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>模板文件</title>
</head>
<body>
    
        <h1>我是content1.html 模板文件中的内容</h1>
    
</body>
</html>
./content2.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>模板文件</title>
</head>
<body>
    
    <h1>我是content2.html 模板文件中的内容</h1>

</body>
</html>
./main.go
package main
import (
    "html/template"
    "math/rand"
    "net/http"
    "time"
)
func handler(w http.ResponseWriter, r *http.Request) {
    rand.Seed(time.Now().UnixNano())
    var t *template.Template
    if rand.Intn(5) > 2 {
        t = template.Must(template.ParseFiles("./hello.html", "./content2.html"))
    } else {
        t = template.Must(template.ParseFiles("./hello.html", "./content1.html"))
    }
    t.ExecuteTemplate(w, "model", "")
}
func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}