使用Golang进行前端渲染的最佳实践

使用Golang进行前端渲染的最佳实践 大家好,

我计划用Go构建一个网关,它将在宏观层面执行两个主要任务:

  1. 充当API服务器,隐藏后端运行的微服务。
  2. 渲染用React编写的前端。

前端将是一种单页应用,其网页将动态填充从网关暴露的API返回的REST响应中检索到的数据。

我的问题是,是否可以通过Go服务器来渲染用React编写的前端?基本上,这个服务器将响应REST请求,同时也渲染通过React创建的HTML页面。如果可能的话,是否有任何项目可以参考,以了解如何通过Go服务器渲染用React编写的前端?

此致, Amit

3 回复

你好

一个React应用(Angular、Vue等框架的应用也是如此)最终会被编译成静态应用(HTML、CSS、JS文件),需要一个HTTP服务器来提供这些文件。 这里你可以找到一个使用go-chi路由器来提供静态文件的示例;但总的来说,你可以提供任何你想要的内容。 在谷歌上搜索,你会找到大量的例子。

更多关于使用Golang进行前端渲染的最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


amitmahajan82:

我的问题是,是否可以通过 Go 服务器来渲染用 React 编写的前端?

GitHub - Shpota/goxygen: Generate a modern Web project with Go and Angular, React, or Vue in seconds 🎲

在几秒钟内生成一个包含 Go 和 Angular、React 或 Vue 的现代 Web 项目 🎲 - GitHub - Shpota/goxygen: 在几秒钟内生成一个包含 Go 和 Angular、React 或 Vue 的现代 Web 项目 🎲

?

是的,完全可以通过Go服务器来渲染React编写的前端。以下是几种实现方案:

方案一:使用Go模板嵌入React构建产物

这是最直接的方式,将React构建后的静态文件嵌入到Go模板中:

package main

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

func main() {
    tmpl := template.Must(template.ParseFiles("templates/index.html"))
    
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // 渲染包含React应用的HTML模板
        tmpl.Execute(w, nil)
    })
    
    // 静态文件服务
    fs := http.FileServer(http.Dir("./static"))
    http.Handle("/static/", http.StripPrefix("/static/", fs))
    
    // API路由
    http.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"message": "Hello from Go API"}`))
    })
    
    http.ListenAndServe(":8080", nil)
}

对应的HTML模板:

<!DOCTYPE html>
<html>
<head>
    <title>React + Go App</title>
    <link rel="stylesheet" href="/static/css/main.css">
</head>
<body>
    <div id="root"></div>
    <script src="/static/js/main.js"></script>
</body>
</html>

方案二:使用V8go执行React服务端渲染

如果需要服务端渲染React组件,可以使用V8go:

package main

import (
    "fmt"
    "net/http"
    roguelib "roguelike/lib"
    "roguelike/v8engine"
)

func main() {
    // 初始化V8引擎
    ctx := v8engine.NewContext()
    defer ctx.Close()
    
    // 加载React和你的组件
    ctx.LoadFile("static/js/react.production.min.js")
    ctx.LoadFile("static/js/react-dom-server.production.min.js")
    ctx.LoadFile("static/js/app.js") // 你的React应用
    
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // 执行服务端渲染
        html, err := ctx.Eval(`
            ReactDOMServer.renderToString(
                React.createElement(App, {initialData: {}})
            )
        `)
        
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        
        // 返回完整的HTML
        fullHTML := fmt.Sprintf(`
            <!DOCTYPE html>
            <html>
                <head>
                    <title>SSR React App</title>
                </head>
                <body>
                    <div id="root">%s</div>
                    <script>
                        window.__INITIAL_DATA__ = {};
                    </script>
                    <script src="/static/js/bundle.js"></script>
                </body>
            </html>
        `, html)
        
        w.Header().Set("Content-Type", "text/html")
        w.Write([]byte(fullHTML))
    })
    
    http.ListenAndServe(":8080", nil)
}

方案三:使用Go+Node混合方案

对于复杂的React SSR,可以使用Go调用Node服务:

package main

import (
    "bytes"
    "encoding/json"
    "net/http"
    "net/http/httputil"
    "os/exec"
)

type SSRRequest struct {
    Component string                 `json:"component"`
    Props     map[string]interface{} `json:"props"`
}

func renderReactComponent(w http.ResponseWriter, r *http.Request) {
    var req SSRRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    // 调用Node服务进行渲染
    cmd := exec.Command("node", "ssr-render.js")
    input, _ := json.Marshal(req)
    cmd.Stdin = bytes.NewReader(input)
    
    output, err := cmd.Output()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    w.Header().Set("Content-Type", "text/html")
    w.Write(output)
}

// 反向代理到React开发服务器
func devProxy() http.Handler {
    return httputil.NewSingleHostReverseProxy(
        &url.URL{Scheme: "http", Host: "localhost:3000"},
    )
}

func main() {
    // 生产环境:静态文件+API
    http.Handle("/static/", http.StripPrefix("/static/", 
        http.FileServer(http.Dir("./build/static"))))
    
    http.HandleFunc("/api/", apiHandler)
    http.HandleFunc("/ssr", renderReactComponent)
    
    // 开发环境:代理到React开发服务器
    if os.Getenv("GO_ENV") == "development" {
        http.Handle("/", devProxy())
    } else {
        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            http.ServeFile(w, r, "./build/index.html")
        })
    }
    
    http.ListenAndServe(":8080", nil)
}

方案四:使用现有框架

使用Gin框架示例:

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    r := gin.Default()
    
    // 加载模板
    r.LoadHTMLGlob("templates/*")
    
    // 静态文件
    r.Static("/static", "./static")
    
    // 首页 - 渲染React应用
    r.GET("/", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.html", gin.H{
            "title": "React + Gin App",
        })
    })
    
    // API路由
    api := r.Group("/api")
    {
        api.GET("/users", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"users": []string{"Alice", "Bob"}})
        })
    }
    
    r.Run(":8080")
}

参考项目

  1. go-react-example - Go + React基础示例

    git clone https://github.com/samuel/go-react-example
    
  2. gin-react-ssr - Gin框架React服务端渲染

    git clone https://github.com/olebedev/gin-react-ssr
    
  3. isomorphic-golang - Go同构应用示例

    git clone https://github.com/gernest/isomorphic-golang
    

构建配置示例

React的package.json配置:

{
  "scripts": {
    "build": "react-scripts build",
    "build:go": "react-scripts build && cp -r build/* ../go-app/static/"
  },
  "proxy": "http://localhost:8080/api"
}

Go项目的目录结构:

go-app/
├── main.go
├── static/
│   ├── index.html
│   ├── js/
│   └── css/
├── templates/
│   └── index.html
└── go.mod

这种架构允许Go网关同时处理API请求和前端渲染,React应用通过Go网关的API获取数据,实现完整的应用架构。

回到顶部