• Gin 打包vue或react项目输出文件到程序二进制文件


    Gin 打包vue或react项目输出文件到程序二进制文件

    背景

    前后端分离已成为行业主流,vuereact等项目生成的文件独立在一个单独目录,与后端项目无关。
    实际部署中,通常前面套一个nginx,根据请求返回静态资源或者代理到后端go服务上。
    安装配置一套环境繁琐,加上有时需要部署在windows上,希望借助go的夸平台编译运行+embed嵌入文件能力,实现单个文件部署即可。

    Created with Raphaël 2.3.0 用户请求 nginx 是否是静态资源? dist目录文件 go-app yes no

    nginx配置样例

    server {
        listen 80;
        root /usr/share/nginx/html;
        location / {
            try_files $uri $uri/index.html /index.html;
        }
        location /api {
            proxy_pass http://localhost/to-go-app-server;
            proxy_set_header   X-Forwarded-Proto $scheme;
            proxy_set_header   X-Real-IP         $remote_addr;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解决方案

    1. 示例目录结构

    • app.go为代码文件,同dist一个目录
    • app-server为编译后的单个exe
    .
    ├── app.go
    ├── app-server
    └── dist
        ├── assets
        │   ├── index-43d6e8d0.css
        │   └── index-f5e49ae2.js
        ├── CNAME
        ├── element-plus-logo-small.svg
        ├── favicon.svg
        ├── index.html
        └── vite.svg
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2. 有如下问题要解决:

    • http://exmpale.com/跟路由如何定向到dist/index.html
    • http://exmpale.com/xx.svg 以及 http://exmpale.com/assets/xxxxx.js 这些动态路由如何生成
    • http://exmpale.com/正常业务路由与上面静态文件路由冲突如何处理

    3. 方案探索

    经过参考gin官方github.com/gin-contrib/static的插件,找出以下简单有效的解决方案

    • 使用embed将整个文件夹嵌入
    • 所有请求增加一个中间件, 判断embed.FS总是否存在url中路径的文件
    • 存在使用http.fileserver处理,并中断处理链
    • 不存在处理正常的逻辑
    package main
    
    import (
    	"embed"
    	"io/fs"
    	"net/http"
    
    	"github.com/gin-gonic/gin"
    )
    
    //go:embed dist
    var dist embed.FS
    
    func main() {
    	r := gin.Default()
    	r.Use(ServerStatic("dist", dist))
    
    	r.GET("/ping", func(ctx *gin.Context) {
    		ctx.String(http.StatusOK, "pong")
    	})
    	r.Run("localhost:81")
    }
    
    /*
    假设vue/react项目输出文件夹名字为dist,拷贝到该go文件所在目录下
    注意"dist"前后不能有 /
    r.Use(ServerStatic("dist", dist))
    */
    func ServerStatic(prefix string, embedFs embed.FS) gin.HandlerFunc {
    	return func(ctx *gin.Context) {
    		// 去掉前缀
    		fsys, err := fs.Sub(embedFs, prefix)
    		if err != nil {
    			panic(err)
    		}
    		fs2 := http.FS(fsys)
    		f, err := fs2.Open(ctx.Request.URL.Path)
    		if err != nil {
    			// 判断文件不存在,退出交给其他路由函数
    			ctx.Next()
    			return
    		}
    		defer f.Close()
    		http.FileServer(fs2).ServeHTTP(ctx.Writer, ctx.Request)
    		ctx.Abort()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    效果

    • 访问http://localhost:81/ 返回的是vue页面
    • 访问http://localhost:81/ping 返回的是逻辑处理结果pong
  • 相关阅读:
    什么是SpringMVC
    记录QEMU上模拟ARM运行环境(内核 2.6.30)
    实例036:算素数
    网络通信知识地图
    哈希原理及模拟实现并封装unordered系列关联式容器
    QGraphicsView,QGraphicsScene和QGraphicsItem
    C#实现网络通信共享库NetShare
    PHP:foreach结构
    面试Java高级工程师之Redis总结
    项目资讯丨轻空间中标连云港市首座“多功能声学综合馆”(EPC)
  • 原文地址:https://blog.csdn.net/LeoForBest/article/details/132915581