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

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

  • 背景
  • 解决方案
    • [1. 示例目录结构](#1. 示例目录结构)
    • [2. 有如下问题要解决:](#2. 有如下问题要解决:)
    • [3. 方案探索](#3. 方案探索)
  • 效果

背景

前后端分离已成为行业主流,vuereact等项目生成的文件独立在一个单独目录,与后端项目无关。

实际部署中,通常前面套一个nginx,根据请求返回静态资源或者代理到后端go服务上。

安装配置一套环境繁琐,加上有时需要部署在windows上,希望借助go的夸平台编译运行+embed嵌入文件能力,实现单个文件部署即可。
Created with Raphaël 2.3.0 用户请求 nginx 是否是静态资源? dist目录文件 go-app yes no

nginx配置样例

config 复制代码
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. 示例目录结构

  • 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

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处理,并中断处理链
  • 不存在处理正常的逻辑
go 复制代码
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()
	}
}

效果

  • 访问http://localhost:81/ 返回的是vue页面
  • 访问http://localhost:81/ping 返回的是逻辑处理结果pong
相关推荐
想退休的搬砖人33 分钟前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js
啥子花道1 小时前
Vue3.4 中 v-model 双向数据绑定新玩法详解
前端·javascript·vue.js
清汤饺子1 小时前
实践指南之网页转PDF
前端·javascript·react.js
清灵xmf1 小时前
揭开 Vue 3 中大量使用 ref 的隐藏危机
前端·javascript·vue.js·ref
霸气小男1 小时前
react + antDesign封装图片预览组件(支持多张图片)
前端·react.js
学习路上的小刘2 小时前
vue h5 蓝牙连接 webBluetooth API
前端·javascript·vue.js
&白帝&2 小时前
vue3常用的组件间通信
前端·javascript·vue.js
小白小白从不日白2 小时前
react 组件通讯
前端·react.js
冯宝宝^3 小时前
基于mongodb+flask(Python)+vue的实验室器材管理系统
vue.js·python·flask
cc蒲公英3 小时前
Vue2+vue-office/excel 实现在线加载Excel文件预览
前端·vue.js·excel