先决条件

1.18.31.22.18v16.15.1

创建我们的 Go 项目

首先我们要创建我们的 Go 项目

mkdir go-react-demo
cd go-react-demo
touch main.go

进入全屏模式 退出全屏模式

然后,我们要安装Echo这是一个 Web 框架(类似于 Gin、Fiber 等)

go get github.com/labstack/echo/v4

进入全屏模式 退出全屏模式

使用 echo 创建基本 API 路由端点

main.go
package main

import (
    "net/http"

    "github.com/labstack/echo/v4"
)

func main() {
    e := echo.New()
    e.GET("/api", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":8080"))
}

进入全屏模式 退出全屏模式

GEThttp://localhost:8080/apiHello, World!
curl http:localhost:8080/api # <-- Should output "Hello, World!"

进入全屏模式 退出全屏模式

如果一切正常,接下来我们将使用 Vite 创建我们的 React 应用程序

使用/ Vite 创建你的 React 应用程序

确保您位于根项目目录中,然后运行:

yarn create vite
# Set the "project name" to "web"
# Set the "web framework" to "react" & "react-ts"

进入全屏模式 退出全屏模式

Vite
cd web
yarn install

进入全屏模式 退出全屏模式

package.json
package.jsondevvitevite
  "scripts": {
    "dev": "tsc && vite build --watch", <-- Change dev script to this
    "build": "tsc && vite build",
    "preview": "vite preview"
  },

进入全屏模式 退出全屏模式

devtsc && vite build --watchvite
webyarn devdist
# In go-react-demo/web
yarn run dev

进入全屏模式 退出全屏模式

此时我们的文件夹结构将如下所示:

go-react-demo/
├─ web/
│  ├─ dist/
│  ├─ public/
│  ├─ src/
|  ├─ ...
├─ main.go
├─ go.sum
├─ go.mod

进入全屏模式 退出全屏模式

使用 Echo 服务我们的静态文件

webweb.go
// In go-react-demo/web/web.go

package web

import (

    "embed"
    "github.com/labstack/echo/v4"
)

var (
    //go:embed all:dist
    dist embed.FS
    //go:embed dist/index.html
    indexHTML     embed.FS
    distDirFS     = echo.MustSubFS(dist, "dist")
    distIndexHtml = echo.MustSubFS(indexHTML, "dist")
) 

func RegisterHandlers(e *echo.Echo) {
    e.FileFS("/", "index.html", distIndexHtml)
    e.StaticFS("/", distDirFS)
}

进入全屏模式 退出全屏模式

/web/index.html
RegisterHandlersmain.go
main.gowebRegisterHandlers
package main

import (

    "net/http"

    "go-react-demo/web" # <--- INCLUDE THIS

    "github.com/labstack/echo/v4"

)

func main() {
    e := echo.New() 
    web.RegisterHandlers(e) # <-- INCLUDE THIS
    e.GET("/api", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello world!")
    })
    e.Logger.Fatal(e.Start(":8080"))
}

进入全屏模式 退出全屏模式

现在让我们测试 go 服务器,看看它是否正确地为我们的 react 应用程序的静态资产提供服务。转到项目的根目录并运行:

go run main.go

进入全屏模式 退出全屏模式

现在,如果您在浏览器中访问http://localhost:8080,您应该会看到默认的 vite React 应用程序。

从 React 内部向 Go API 服务器发出请求

GET
// In go-react-demo/web/src/App.tsx
import { useState, useEffect } from "react";
import "./App.css"; 

function App() {
  const [data, setData] = useState("");

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch("http://localhost:8080/api");
      const data = await response.text();
      setData(data);
    };

    fetchData().catch((err) => console.log(err));
  }, []);



  return (
    <div className="App">
      <h1>{data}</h1>
    </div>
  );

}

export default App;

进入全屏模式 退出全屏模式

现在我们需要重新生成 React 静态文件,因为我们已经进行了更改。

# assuming you're currently at the rootDirectory (go-react-demo)
cd web && yarn run dev # Generates the new static assets

进入全屏模式 退出全屏模式

然后我们需要运行 go server 来提供文件

cd .. && go run main.go

进入全屏模式 退出全屏模式

如果我们访问http://localhost:8080,你应该会看到来自 Go API 服务器的“Hello world”

真是糟糕的开发体验

我敢肯定你注意到,总是运行两个具有不同进程的终端是一种非常糟糕的开发体验,不要担心我有解决方案!

airnodemongoairgo run main.go
air
go install github.com/cosmtrek/air@latest

进入全屏模式 退出全屏模式

air
#You should be in the root directory of the go-react-demo project
air init # Should output a `.air.toml`

进入全屏模式 退出全屏模式

wsldev.sh
touch dev.sh # creates the file

进入全屏模式 退出全屏模式

dev.sh
#!/bin/sh

cd web && yarn dev & air && fg

进入全屏模式 退出全屏模式

这将在一个终端中同时运行 go api 服务器和 vite 构建服务器

编译二进制文件

现在,关键时刻:编译包含 React 应用程序的二进制文件只需运行

go build main.go

进入全屏模式 退出全屏模式

如果您尝试从 WSL 构建 Windows 二进制文件:

env GOOS=windows GOARCH=amd64 go build main.go
# You may have a different $GOARCH so please do some research

进入全屏模式 退出全屏模式

恭喜!你已经创建了一个包含 API 和 React 应用程序的单一 go 二进制文件!