Golang HTML 模板使用指南示例詳解
更新時(shí)間:2025年01月14日 09:30:44 作者:老大白菜
本文詳細(xì)介紹了Golang HTML模板的使用方法,包括基礎(chǔ)模板、高級模板、完整應(yīng)用示例、CSS樣式、JavaScript交互等,文章強(qiáng)調(diào)了模板組織、代碼復(fù)用、語義化HTML、響應(yīng)式設(shè)計(jì)、性能優(yōu)化等最佳實(shí)踐,感興趣的朋友跟隨小編一起看看吧
Golang HTML 模板使用指南
1. 基礎(chǔ)模板示例
1.1 簡單頁面模板
<!-- templates/layout.html --> <!DOCTYPE html> <html> <head> <title>{{.Title}}</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/css/style.css" rel="external nofollow" > </head> <body> <header> <h1>{{.Title}}</h1> <nav> <a href="/" rel="external nofollow" >首頁</a> <a href="/about" rel="external nofollow" >關(guān)于</a> <a href="/contact" rel="external nofollow" >聯(lián)系我們</a> </nav> </header> <main> {{template "content" .}} </main> <footer> <p>© {{.Year}} 我的網(wǎng)站</p> </footer> </body> </html>
// main.go package main import ( "html/template" "net/http" "time" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { tmpl := template.Must(template.ParseFiles("templates/layout.html")) data := struct { Title string Year int }{ Title: "我的網(wǎng)站", Year: time.Now().Year(), } tmpl.Execute(w, data) }) http.ListenAndServe(":8080", nil) }
1.2 列表渲染
<!-- templates/products.html --> {{define "content"}} <div class="products"> <h2>產(chǎn)品列表</h2> <div class="product-grid"> {{range .Products}} <div class="product-card"> <img src="{{.Image}}" alt="{{.Name}}"> <h3>{{.Name}}</h3> <p>{{.Description}}</p> <div class="price">{{formatPrice .Price}}</div> {{if .InStock}} <button class="buy-btn">購買</button> {{else}} <button class="out-of-stock" disabled>缺貨</button> {{end}} </div> {{end}} </div> </div> {{end}}
type Product struct { Name string Description string Price float64 Image string InStock bool } func productsHandler(w http.ResponseWriter, r *http.Request) { funcMap := template.FuncMap{ "formatPrice": func(price float64) string { return fmt.Sprintf("¥%.2f", price) }, } tmpl := template.New("layout.html").Funcs(funcMap) tmpl = template.Must(tmpl.ParseFiles( "templates/layout.html", "templates/products.html", )) data := struct { Title string Year int Products []Product }{ Title: "產(chǎn)品列表", Year: time.Now().Year(), Products: []Product{ { Name: "商品1", Description: "這是商品1的描述", Price: 99.99, Image: "/static/images/product1.jpg", InStock: true, }, // 更多商品... }, } tmpl.Execute(w, data) }
2. 高級模板示例
2.1 嵌套模板
<!-- templates/components/header.html --> {{define "header"}} <header class="site-header"> <div class="logo"> <img src="/static/images/logo.png" alt="Logo"> </div> <nav class="main-nav"> <ul> {{range .NavItems}} <li class="{{if eq $.CurrentPage .Link}}active{{end}}"> <a href="{{.Link}}" rel="external nofollow" >{{.Text}}</a> </li> {{end}} </ul> </nav> {{if .User}} <div class="user-menu"> <span>歡迎, {{.User.Name}}</span> <a href="/logout" rel="external nofollow" >退出</a> </div> {{else}} <div class="auth-buttons"> <a href="/login" rel="external nofollow" class="btn btn-login">登錄</a> <a href="/register" rel="external nofollow" class="btn btn-register">注冊</a> </div> {{end}} </header> {{end}}
2.2 表單處理
<!-- templates/form.html --> {{define "content"}} <div class="form-container"> <h2>{{.FormTitle}}</h2> {{with .FormError}} <div class="error-message">{{.}}</div> {{end}} <form method="POST" action="{{.FormAction}}" enctype="multipart/form-data"> {{range .Fields}} <div class="form-group"> <label for="{{.ID}}">{{.Label}}{{if .Required}}*{{end}}</label> {{if eq .Type "text"}} <input type="text" id="{{.ID}}" name="{{.Name}}" value="{{.Value}}" {{if .Required}}required{{end}} {{with .Pattern}}pattern="{{.}}"{{end}}> {{else if eq .Type "textarea"}} <textarea id="{{.ID}}" name="{{.Name}}" {{if .Required}}required{{end}}>{{.Value}}</textarea> {{else if eq .Type "select"}} <select id="{{.ID}}" name="{{.Name}}" {{if .Required}}required{{end}}> {{range .Options}} <option value="{{.Value}}" {{if eq .Value $.Selected}}selected{{end}}> {{.Text}} </option> {{end}} </select> {{end}} {{with .Error}} <span class="field-error">{{.}}</span> {{end}} </div> {{end}} <div class="form-actions"> <button type="submit" class="btn btn-primary">{{.SubmitText}}</button> <button type="reset" class="btn btn-secondary">重置</button> </div> </form> </div> {{end}}
2.3 分頁組件
<!-- templates/components/pagination.html --> {{define "pagination"}} <div class="pagination"> {{if gt .TotalPages 1}} {{if gt .CurrentPage 1}} <a href="?page=1" rel="external nofollow" class="page-link first">«</a> <a href="?page={{subtract .CurrentPage 1}}" rel="external nofollow" class="page-link prev">‹</a> {{end}} {{range $i := range (sequence .TotalPages)}} {{if and (ge $i (subtract $.CurrentPage 2)) (le $i (add $.CurrentPage 2))}} <a href="?page={{add $i 1}}" rel="external nofollow" class="page-link {{if eq $i (subtract $.CurrentPage 1)}}active{{end}}"> {{add $i 1}} </a> {{end}} {{end}} {{if lt .CurrentPage .TotalPages}} <a href="?page={{add .CurrentPage 1}}" rel="external nofollow" class="page-link next">›</a> <a href="?page={{.TotalPages}}" rel="external nofollow" class="page-link last">»</a> {{end}} {{end}} </div> {{end}}
3. 完整應(yīng)用示例
3.1 博客系統(tǒng)模板
<!-- templates/blog/list.html --> {{define "content"}} <div class="blog-list"> <div class="filters"> <div class="search"> <input type="text" placeholder="搜索文章..." value="{{.Query}}" id="searchInput"> </div> <div class="categories"> {{range .Categories}} <a href="/blog?category={{.Slug}}" rel="external nofollow" class="category {{if eq $.CurrentCategory .Slug}}active{{end}}"> {{.Name}} ({{.Count}}) </a> {{end}} </div> </div> <div class="articles"> {{range .Posts}} <article class="post-card"> {{if .Image}} <div class="post-image"> <img src="{{.Image}}" alt="{{.Title}}"> </div> {{end}} <div class="post-content"> <h2><a href="/blog/{{.Slug}}" rel="external nofollow" >{{.Title}}</a></h2> <div class="post-meta"> <span class="author">{{.Author}}</span> <span class="date">{{formatDate .CreatedAt "2006-01-02"}}</span> <span class="category">{{.Category}}</span> </div> <p class="excerpt">{{truncate .Content 200}}</p> <div class="tags"> {{range .Tags}} <a href="/blog?tag={{.}}" rel="external nofollow" class="tag">{{.}}</a> {{end}} </div> </div> </article> {{else}} <div class="no-posts"> 暫無文章 </div> {{end}} </div> {{template "pagination" .Pagination}} </div> {{end}}
type BlogData struct { Query string CurrentCategory string Categories []Category Posts []Post Pagination Pagination } type Category struct { Name string Slug string Count int } type Post struct { Title string Slug string Content string Author string Image string Category string Tags []string CreatedAt time.Time } type Pagination struct { CurrentPage int TotalPages int TotalItems int } func blogHandler(w http.ResponseWriter, r *http.Request) { funcMap := template.FuncMap{ "formatDate": func(t time.Time, layout string) string { return t.Format(layout) }, "truncate": func(s string, l int) string { if len(s) <= l { return s } return s[:l] + "..." }, } tmpl := template.New("layout.html").Funcs(funcMap) tmpl = template.Must(tmpl.ParseFiles( "templates/layout.html", "templates/blog/list.html", "templates/components/pagination.html", )) data := BlogData{ Query: r.URL.Query().Get("q"), CurrentCategory: r.URL.Query().Get("category"), Categories: []Category{ {Name: "技術(shù)", Slug: "tech", Count: 10}, {Name: "生活", Slug: "life", Count: 5}, }, Posts: []Post{ { Title: "示例文章", Slug: "example-post", Content: "這是一篇示例文章的內(nèi)容...", Author: "作者名", Category: "tech", Tags: []string{"Go", "Web"}, CreatedAt: time.Now(), }, }, Pagination: Pagination{ CurrentPage: 1, TotalPages: 5, TotalItems: 45, }, } tmpl.Execute(w, data) }
4. CSS 樣式示例
/* static/css/style.css */ /* 基礎(chǔ)樣式 */ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.6; margin: 0; padding: 0; color: #333; } /* 頭部樣式 */ .site-header { background: #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 1rem; } .main-nav ul { list-style: none; display: flex; gap: 1rem; } /* 產(chǎn)品卡片樣式 */ .product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 2rem; padding: 2rem; } .product-card { border: 1px solid #eee; border-radius: 8px; overflow: hidden; transition: transform 0.2s; } .product-card:hover { transform: translateY(-5px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); } /* 表單樣式 */ .form-container { max-width: 600px; margin: 2rem auto; padding: 2rem; background: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .form-group { margin-bottom: 1.5rem; } /* 博客列表樣式 */ .blog-list { max-width: 1200px; margin: 0 auto; padding: 2rem; } .post-card { display: grid; grid-template-columns: 200px 1fr; gap: 1.5rem; margin-bottom: 2rem; padding: 1rem; background: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* 分頁樣式 */ .pagination { display: flex; justify-content: center; gap: 0.5rem; margin: 2rem 0; } .page-link { padding: 0.5rem 1rem; border: 1px solid #ddd; border-radius: 4px; color: #333; text-decoration: none; } .page-link.active { background: #007bff; color: #fff; border-color: #007bff; }
5. JavaScript 交互示例
// static/js/main.js document.addEventListener('DOMContentLoaded', function() { // 搜索功能 const searchInput = document.getElementById('searchInput'); if (searchInput) { searchInput.addEventListener('input', debounce(function(e) { const query = e.target.value; window.location.href = `/blog?q=${encodeURIComponent(query)}`; }, 500)); } // 表單驗(yàn)證 const forms = document.querySelectorAll('form'); forms.forEach(form => { form.addEventListener('submit', function(e) { const requiredFields = form.querySelectorAll('[required]'); let valid = true; requiredFields.forEach(field => { if (!field.value.trim()) { valid = false; field.classList.add('error'); } else { field.classList.remove('error'); } }); if (!valid) { e.preventDefault(); alert('請?zhí)顚懰斜靥钭侄?); } }); }); }); // 工具函數(shù) function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }
總結(jié)
模板組織
- 使用嵌套模板實(shí)現(xiàn)代碼復(fù)用
- 保持模板結(jié)構(gòu)清晰
- 合理使用模板函數(shù)
最佳實(shí)踐
- 使用語義化 HTML
- 保持 CSS 模塊化
- 實(shí)現(xiàn)響應(yīng)式設(shè)計(jì)
- 添加適當(dāng)?shù)慕换バЧ?/li>
性能優(yōu)化
- 緩存編譯后的模板
- 壓縮靜態(tài)資源
- 使用 CDN 加速
- 實(shí)現(xiàn)懶加載
到此這篇關(guān)于Golang HTML 模板使用指南的文章就介紹到這了,更多相關(guān)Golang HTML 模板內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang包循環(huán)引用的幾種解決方案總結(jié)
golang有包循環(huán)引用問題,用過的應(yīng)該都知道,下面這篇文章主要給大家介紹了關(guān)于golang包循環(huán)引用的幾種解決方案,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09簡化Go開發(fā)提高生產(chǎn)力的強(qiáng)大工具及使用詳解
作為?Go?開發(fā)人員,應(yīng)該都知道維持簡潔高效開發(fā)工作流程的重要性,為了提高工作效率和代碼質(zhì)量,簡化開發(fā)流程并自動(dòng)執(zhí)行重復(fù)性任務(wù)至關(guān)重要,在本文中,我們將探討一些強(qiáng)大的工具和技術(shù),它們將簡化?Go?開發(fā)過程,助力您的編碼之旅2023-10-105個(gè)可以在Golang中優(yōu)化代碼以提高性能的技巧分享
作為一名軟件工程師,確保你的代碼高效且性能良好是非常重要的。本文主要和大家分享5個(gè)可以在Golang中優(yōu)化代碼以提高性能的技巧,希望對大家有所幫助2023-03-03Golang實(shí)現(xiàn)Java虛擬機(jī)之解析class文件詳解
這篇文章主要為大家詳細(xì)介紹了Golang實(shí)現(xiàn)Java虛擬機(jī)之解析class文件的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01Golang Printf,Sprintf,Fprintf 格式化詳解
這篇文章主要介紹了Golang Printf,Sprintf,Fprintf 格式化詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03