Golang与HTMX已可用于生产环境

Golang与HTMX已可用于生产环境 大家好,

有没有人在生产环境中尝试过使用 Go 和 HTMX。

6 回复

感谢详细的解释

更多关于Golang与HTMX已可用于生产环境的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个非常普遍的问题。HTMX 只是一种处理客户端用户交互、如何向服务器发送请求以及服务器应如何响应的方法。Golang 拥有 template/html 库,可以很好地处理对这些请求的回复。如果你做一些搜索,我猜你能找到很多使用 Go + HTMX 的例子。

我研究了一下 htmx。 我认为它绝对已经可以用于生产环境,但老实说,我无法理解它为何如此受追捧。 也许是我太笨了,没能理解它要解决什么问题,但在我测试时,用原生 JavaScript 写几行代码就差不多能模仿 htmx 的功能了。

我尚未在生产环境中使用过它,也不是一个成熟的Go开发者。我是一名全栈JavaScript开发者,曾参与并启动过许多单页React应用。我认为HTMX可能是一个更好的起点,至少比目前市面上80%基于单页应用构建的应用要好。

我构建了一个示例应用(Conduit,一个遵循TodoMVC传统的Medium克隆),并且发现它非常令人满意。它使得快速启动和运行变得非常容易,减少了编译、打包和交付JavaScript应用的复杂性。

我认为它的缺点是,它要求遵循一些并非所有人都非常熟悉的HTML原则。例如,当服务器响应错误时,post/delete操作需要返回不同的状态码302。在这种情况下,htmx的反应可能与你预期的不同。这是你早期就会遇到的问题,也是少数可能让你感到困惑的事情之一。

我还用Clojure后端构建了相同的应用,在这两种情况下我发现,与构建前端和与后端协作的API层相比,你花费了更多时间处理后端事务。

Karl:

也许我太笨了,理解不了它要解决的问题,但当我测试时,我似乎能用几行原生 JavaScript 模仿出 htmx 的效果。

我当时的反应完全一样。我看了看,心想:“好吧,这有点像其他前端框架,但用了不同的绑定语法,并且强调服务端渲染??” 我搜索了“为什么使用 htmx”,然后找到了像这样的内容

  • 响应式组件:htmx 引入了一个称为“响应式组件”的新概念,它允许在不重新加载整个页面的情况下高效地更新 DOM。这为用户带来了更快、更无缝的交互体验。

响应式组件?真的,我们生活在未来。还有:

使用 htmx 的原因之一是它能够将 HTML、CSS 和 JavaScript 集成到一个单一、内聚的库中。这使得开发人员能够创建易于维护和更新的丰富、交互式用户界面。

React/Angular/等应用难道不是将 HTML、CSS 和 JavaScript 集成到一个单一的库中吗?

但是那些喜欢灵活性——例如,使用不受 JavaScript 影响的其他服务端语言——的后端开发人员呢?htmx 通过让你能够渲染 HTML 内容,并使用你选择的任何服务端语言提供类似 SPA 的体验,从而解决了这个问题。

除非我误解了它酷在哪里,否则我从使用 Razor 之前的 ASPX 应用时代就开始这么做了(即:在服务器上渲染一块 HTML 并增量更新我的页面,而不是完全重新加载或使用 JavaScript 根据 JSON 响应来操作 DOM)。

另外——说它“不是 JavaScript”这个观点我不理解。在我看来它就像是 JavaScript。再说一次——它似乎只是一种不同的绑定语法,并侧重于服务端渲染。并不完全是它所标榜的那种范式转变。而且每个写博客谈论它的人似乎都在重复同样的话,却没有阐明为什么那是真的。

是的,Go 与 HTMX 的组合已完全可用于生产环境。许多团队正在使用这种技术栈构建高性能、动态的 Web 应用。

Go 作为后端,提供高效、可靠的 API 和数据服务;HTMX 在前端处理动态内容更新,无需复杂的 JavaScript 框架。这种组合降低了前端复杂度,同时保持了优秀的用户体验。

以下是一个简单的生产级示例,展示如何使用 Go 处理 HTMX 请求:

package main

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

type Task struct {
    ID    int
    Title string
    Done  bool
}

var tasks = []Task{
    {ID: 1, Title: "学习 Go", Done: true},
    {ID: 2, Title: "集成 HTMX", Done: false},
}

func main() {
    tmpl := template.Must(template.ParseGlob("templates/*.html"))
    
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        tmpl.ExecuteTemplate(w, "index.html", tasks)
    })
    
    http.HandleFunc("/tasks/", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case "POST":
            title := r.FormValue("title")
            newTask := Task{
                ID:    len(tasks) + 1,
                Title: title,
                Done:  false,
            }
            tasks = append(tasks, newTask)
            
            w.Header().Set("HX-Trigger", "taskAdded")
            tmpl.ExecuteTemplate(w, "task.html", newTask)
            
        case "DELETE":
            idStr := r.PathValue("id")
            id, _ := strconv.Atoi(idStr)
            
            for i, task := range tasks {
                if task.ID == id {
                    tasks = append(tasks[:i], tasks[i+1:]...)
                    break
                }
            }
            w.WriteHeader(http.StatusOK)
        }
    })
    
    http.HandleFunc("/tasks/{id}/toggle", func(w http.ResponseWriter, r *http.Request) {
        idStr := r.PathValue("id")
        id, _ := strconv.Atoi(idStr)
        
        for i := range tasks {
            if tasks[i].ID == id {
                tasks[i].Done = !tasks[i].Done
                tmpl.ExecuteTemplate(w, "task.html", tasks[i])
                break
            }
        }
    })
    
    http.ListenAndServe(":8080", nil)
}

对应的 HTML 模板(templates/index.html):

<!DOCTYPE html>
<html>
<head>
    <script src="https://unpkg.com/htmx.org@1.9.10"></script>
</head>
<body>
    <h1>任务列表</h1>
    
    <div id="tasks">
        {{range .}}
            {{template "task.html" .}}
        {{end}}
    </div>
    
    <form hx-post="/tasks/" hx-target="#tasks" hx-swap="beforeend">
        <input type="text" name="title" placeholder="新任务" required>
        <button type="submit">添加</button>
    </form>
</body>
</html>

任务模板(templates/task.html):

<div class="task" id="task-{{.ID}}">
    <input type="checkbox" 
           hx-post="/tasks/{{.ID}}/toggle"
           hx-target="closest .task"
           hx-swap="outerHTML"
           {{if .Done}}checked{{end}}>
    
    <span class="{{if .Done}}done{{end}}">{{.Title}}</span>
    
    <button hx-delete="/tasks/{{.ID}}"
            hx-target="closest .task"
            hx-swap="delete">删除</button>
</div>

这个示例展示了完整的 CRUD 操作:

  1. 使用 HTMX 属性处理 AJAX 请求
  2. 部分内容更新(无需整页刷新)
  3. 响应式界面更新
  4. 后端状态管理

在生产环境中,建议添加以下优化:

  • 数据库集成(PostgreSQL/MySQL)
  • 会话管理
  • 中间件支持(日志、恢复、安全)
  • 适当的错误处理
  • 静态文件服务

Go 的 net/http 包与 HTMX 的配合非常直接,两者都能提供优秀的性能和开发体验。

回到顶部