手把手教你用 React 和 Go 部署全栈项目
在本指南中,我们将学习如何结合使用 React (通过 Vite) 构建前端用户界面,并使用 Go (Golang) 创建一个高效的后端服务来提供静态文件。这种架构非常适合构建单页应用 (SPA),其中前端负责所有 UI 逻辑,而后端则提供数据和静态资源。
所有源码可以在这里找到:github.com/yincongcyin...

前端部分:使用 React (Vite)
我们将使用 Vite 来快速启动一个 React 项目。Vite 是一个现代化的前端构建工具,提供极速的开发体验和优化的生产构建。
1. 创建 React 项目
首先,打开你的终端或命令行工具,运行以下命令来创建一个新的 React 项目:
Bash
sql
npm create vite@latest my-react-app -- --template react

npm create vite@latest
: 这是一个 npm 命令,用于使用最新版本的 Vite 创建一个新项目。my-react-app
: 这是你的项目文件夹的名称。你可以将其替换为你喜欢的任何名称。--template react
: 这告诉 Vite 使用 React 模板来初始化项目。
2. 进入项目目录
项目创建完成后,你需要进入到新创建的项目目录中:
Bash
bash
cd my-react-app
3. 安装依赖
在项目目录中,安装项目所需的全部 Node.js 依赖:
Bash
npm install

这会根据 package.json
文件中的定义安装所有必要的库。
4. 构建前端静态文件
当你准备好部署前端应用时,你需要将其构建为生产就绪的静态文件。运行以下命令:
Bash
arduino
npm run build

此命令将会在项目根目录下创建一个 dist
文件夹,其中包含所有优化的 HTML、CSS 和 JavaScript 文件。这些文件就是你的前端应用的静态资产。
5. 移动前端静态文件到目标路径
为了让 Go 后端能够提供这些静态文件,你需要将 dist
文件夹中的内容移动到 Go 项目可以访问的位置。假设你的 Go 项目在 my-react-app
的父目录中,并且 Go 项目的静态文件目录名为 test
,你可以使用以下命令:
Bash
bash
mv dist/* ../../test
mv dist/*
: 将dist
目录下的所有文件和文件夹移动。../../test
: 这是目标路径,表示从当前目录向上两级,然后进入名为test
的目录。请根据你的实际项目结构调整此路径。
后端部分:使用 Go 提供静态文件
Go 后端将负责托管前端的静态文件,并在所有非静态文件请求时提供 index.html
,这对于单页应用至关重要。
Go 项目结构
确保你的 Go 项目中有一个名为 test
的文件夹,用于存放 React 构建后的静态文件。例如:
go
your-go-project/
├── main.go
└── test/
├── index.html
├── assets/
└── ... (其他 React 构建的文件)
Go 代码解析
以下是你的 Go 后端代码,我们将逐一解释其关键部分:
Go
go
package main
import (
"bytes"
"embed"
"io/fs"
"net/http"
"time"
)
//go:embed test/*
var staticFiles embed.FS
//go:embed test/*
: 这是一个 Go 编译器的指令。它告诉编译器将test
目录下的所有文件和子目录嵌入到最终编译的二进制文件中。这意味着你的 Go 应用运行时,不再需要外部的test
文件夹,所有的前端静态文件都打包在 Go 可执行文件中。var staticFiles embed.FS
: 声明一个embed.FS
类型的变量staticFiles
,它将存储嵌入的文件系统。
Go
scss
func View() http.HandlerFunc {
distFS, _ := fs.Sub(staticFiles, "test")
staticHandler := http.FileServer(http.FS(distFS))
return func(w http.ResponseWriter, r *http.Request) {
if fileExists(distFS, r.URL.Path[1:]) {
staticHandler.ServeHTTP(w, r)
return
}
fileBytes, err := fs.ReadFile(distFS, "index.html")
if err != nil {
http.Error(w, "index.html not found", http.StatusInternalServerError)
return
}
reader := bytes.NewReader(fileBytes)
http.ServeContent(w, r, "index.html", time.Now(), reader)
}
}
func View() http.HandlerFunc
: 定义了一个返回http.HandlerFunc
的函数,这将作为 HTTP 请求的处理程序。distFS, _ := fs.Sub(staticFiles, "test")
: 创建一个fs.FS
接口的子集,使其只暴露test
目录下的文件。这是因为embed
会将test
本身作为根目录的一部分嵌入,所以我们需要一个子集来正确地提供文件。staticHandler := http.FileServer(http.FS(distFS))
: 创建一个标准的 Gohttp.FileServer
,它会从distFS
中查找和提供文件。if fileExists(distFS, r.URL.Path[1:])
: 对于每个传入的请求,它首先检查请求的路径(去除开头的/
)是否对应一个实际存在于嵌入文件系统中的文件。staticHandler.ServeHTTP(w, r)
: 如果文件存在,就由staticHandler
来处理并返回该文件。fileBytes, err := fs.ReadFile(distFS, "index.html")
: 如果请求的路径不是一个具体的文件(例如,用户直接访问/
或刷新了应用的内部路由),则尝试读取index.html
。这对于单页应用至关重要,因为 React 路由通常在客户端处理,所有路由都应该返回index.html
。http.ServeContent(w, r, "index.html", time.Now(), reader)
: 将index.html
的内容作为响应返回给客户端。
Go
go
func fileExists(fsys fs.FS, path string) bool {
f, err := fsys.Open(path)
if err != nil {
return false
}
defer f.Close()
info, err := f.Stat()
if err != nil || info.IsDir() {
return false
}
return true
}
fileExists
函数: 这是一个辅助函数,用于检查给定路径的文件是否存在且不是一个目录。
Go
go
func main() {
http.Handle("/", View())
err := http.ListenAndServe(":18888", nil)
if err != nil {
panic(err)
}
}
http.Handle("/", View())
: 将根路径 (/
) 的所有请求都交给View()
函数返回的处理器处理。http.ListenAndServe(":18888", nil)
: 启动 HTTP 服务器,监听18888
端口。nil
表示使用默认的ServeMux
。
运行 Go 后端
在 Go 项目的根目录下(与 main.go
文件同级),运行以下命令来启动 Go 服务器:
Bash
go
go run main.go
现在,你的 Go 后端将会在 http://localhost:18888
监听请求,并提供你的 React 前端应用。

部署流程总结
- 开发 React 前端 : 在
my-react-app
目录中进行 React 开发,并使用npm run dev
进行本地开发和测试。 - 构建 React 前端 : 准备部署时,运行
npm run build
生成生产就绪的静态文件到dist
目录。 - 移动静态文件 : 将
dist
目录中的内容移动到 Go 项目的test
目录。 - 运行 Go 后端 : 在 Go 项目目录中运行
go run main.go
或构建 Go 可执行文件并运行。
通过这个设置,你就可以拥有一个高效且易于部署的全栈应用程序。