Golang中React服务端渲染(SSR)的实现与优化
Golang中React服务端渲染(SSR)的实现与优化 你好,
我正在尝试用 React 制作一个 SSR 应用,但不知道从哪里开始。我以前用 Node(ExpressJS)做过,将我的 React 应用“渲染成字符串”,然后将输出“放置”到我的 HTML 中。
有人做过类似的项目吗?或者知道如何在 Go 中将 React 应用“渲染”成字符串吗?
谢谢!
1 回复
更多关于Golang中React服务端渲染(SSR)的实现与优化的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中实现React服务端渲染(SSR)主要有两种方案:
方案一:使用Go执行Node.js(推荐)
这是最实用的方法,通过Go调用Node.js的V8引擎来执行React渲染:
package main
import (
"context"
"fmt"
"os"
"os/exec"
"time"
)
type SSRResult struct {
HTML string
Error error
}
func renderReactWithNode(componentPath, props string) (string, error) {
// 创建Node.js渲染脚本
renderScript := fmt.Sprintf(`
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const App = require('%s').default;
const props = %s;
const element = React.createElement(App, props);
const html = ReactDOMServer.renderToString(element);
process.stdout.write(html);
`, componentPath, props)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "node", "-e", renderScript)
cmd.Env = append(os.Environ(), "NODE_ENV=production")
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("Node渲染失败: %v", err)
}
return string(output), nil
}
func main() {
props := `{"title": "Go SSR示例", "count": 42}`
html, err := renderReactWithNode("./components/App.jsx", props)
if err != nil {
panic(err)
}
// 嵌入到HTML模板
fullHTML := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head>
<title>SSR应用</title>
</head>
<body>
<div id="root">%s</div>
<script>
window.__INITIAL_STATE__ = %s;
</script>
<script src="/static/bundle.js"></script>
</body>
</html>
`, html, props)
fmt.Println(fullHTML)
}
方案二:使用Go的JavaScript运行时
使用支持React的Go JavaScript运行时,如goja或v8go:
package main
import (
"fmt"
"github.com/dop251/goja"
"io/ioutil"
)
func renderReactWithGoja() (string, error) {
// 读取React和ReactDOM库
reactSource, _ := ioutil.ReadFile("./node_modules/react/umd/react.production.min.js")
reactDOMSource, _ := ioutil.ReadFile("./node_modules/react-dom/umd/react-dom-server.browser.production.min.js")
vm := goja.New()
// 注入React和ReactDOM
_, err := vm.RunString(string(reactSource))
if err != nil {
return "", err
}
_, err = vm.RunString(string(reactDOMSource))
if err != nil {
return "", err
}
// 定义React组件
componentCode := `
const MyApp = ({ title }) => React.createElement('h1', null, title);
const element = React.createElement(MyApp, { title: 'Go SSR' });
ReactDOMServer.renderToString(element);
`
result, err := vm.RunString(componentCode)
if err != nil {
return "", err
}
return result.String(), nil
}
优化方案:预渲染缓存
package main
import (
"sync"
"time"
)
type SSRCache struct {
mu sync.RWMutex
cache map[string]*CacheEntry
}
type CacheEntry struct {
HTML string
Timestamp time.Time
ExpiresAt time.Time
}
func NewSSRCache(ttl time.Duration) *SSRCache {
return &SSRCache{
cache: make(map[string]*CacheEntry),
}
}
func (c *SSRCache) Get(key string) (string, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
entry, exists := c.cache[key]
if !exists || time.Now().After(entry.ExpiresAt) {
return "", false
}
return entry.HTML, true
}
func (c *SSRCache) Set(key, html string, ttl time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
c.cache[key] = &CacheEntry{
HTML: html,
Timestamp: time.Now(),
ExpiresAt: time.Now().Add(ttl),
}
}
// 使用示例
func renderWithCache(cache *SSRCache, component, props string) (string, error) {
cacheKey := fmt.Sprintf("%s:%s", component, props)
if html, found := cache.Get(cacheKey); found {
return html, nil
}
html, err := renderReactWithNode(component, props)
if err != nil {
return "", err
}
cache.Set(cacheKey, html, 5*time.Minute)
return html, nil
}
完整HTTP服务器示例
package main
import (
"encoding/json"
"net/http"
"time"
)
func main() {
cache := NewSSRCache(5 * time.Minute)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 从请求中提取props
props := map[string]interface{}{
"path": r.URL.Path,
"userAgent": r.UserAgent(),
}
propsJSON, _ := json.Marshal(props)
// 渲染React组件
html, err := renderWithCache(cache, "./components/App.jsx", string(propsJSON))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 发送完整HTML响应
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write([]byte(html))
})
http.ListenAndServe(":8080", nil)
}
这些示例展示了在Go中实现React SSR的核心方法。第一种方案通过调用Node.js是最稳定和兼容性最好的,第二种方案使用Go JavaScript运行时可以减少外部依赖,但需要处理React库的兼容性问题。

