当前位置: 代码网 > it编程>前端脚本>Golang > Golang HTML 模板使用指南示例详解

Golang HTML 模板使用指南示例详解

2025年02月13日 Golang 我要评论
golang html 模板使用指南1. 基础模板示例1.1 简单页面模板<!-- templates/layout.html --><!doctype html><ht

golang html 模板使用指南

1. 基础模板示例

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" >关于</a>
            <a href="/contact" rel="external nofollow" >联系我们</a>
        </nav>
    </header>
    <main>
        {{template "content" .}}
    </main>
    <footer>
        <p>&copy; {{.year}} 我的网站</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: "我的网站",
            year:  time.now().year(),
        }
        tmpl.execute(w, data)
    })
    http.listenandserve(":8080", nil)
}

1.2 列表渲染

<!-- templates/products.html -->
{{define "content"}}
<div class="products">
    <h2>产品列表</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: "产品列表",
        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">&laquo;</a>
            <a href="?page={{subtract .currentpage 1}}" rel="external nofollow"  class="page-link prev">&lsaquo;</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">&rsaquo;</a>
            <a href="?page={{.totalpages}}" rel="external nofollow"  class="page-link last">&raquo;</a>
        {{end}}
    {{end}}
</div>
{{end}}

3. 完整应用示例

3.1 博客系统模板

<!-- 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: "技术", slug: "tech", count: 10},
            {name: "生活", slug: "life", count: 5},
        },
        posts: []post{
            {
                title:     "示例文章",
                slug:      "example-post",
                content:   "这是一篇示例文章的内容...",
                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 */
/* 基础样式 */
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;
}
/* 产品卡片样式 */
.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));
    }
    // 表单验证
    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('请填写所有必填字段');
            }
        });
    });
});
// 工具函数
function debounce(func, wait) {
    let timeout;
    return function executedfunction(...args) {
        const later = () => {
            cleartimeout(timeout);
            func(...args);
        };
        cleartimeout(timeout);
        timeout = settimeout(later, wait);
    };
}

总结

模板组织

  • 使用嵌套模板实现代码复用
  • 保持模板结构清晰
  • 合理使用模板函数

最佳实践

  • 使用语义化 html
  • 保持 css 模块化
  • 实现响应式设计
  • 添加适当的交互效果

性能优化

  • 缓存编译后的模板
  • 压缩静态资源
  • 使用 cdn 加速
  • 实现懒加载

到此这篇关于golang html 模板使用指南的文章就介绍到这了,更多相关golang html 模板内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com