需用自定义 http.Handler 实现 SPA 路由 fallback:先尝试从 embed.FS 读取静态文件,未命中且路径无扩展名时返回 dist/index.html;构建前须确保 Vue 已输出 dist/,且 publicPath 配置与 Go 服务路径对齐。

用 http.FileServer 服务 Vue 构建产物时路径要对齐
Vue CLI 或 Vite 构建后默认输出到 dist/ 目录,其中 index.html 是入口,所有 JS/CSS 资源都按相对路径引用。Golang 的 http.FileServer 默认不处理前端路由(比如 /dashboard),直接访问会 404。
关键不是“能不能 serve”,而是“怎么让 /xxx 这类非静态资源路径也 fallback 到 index.html”。常见错误是只写 http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(...))),结果路由跳转后刷新就 404。
- 必须把
dist/整体注册为根路径的文件服务,且启用 fallback:用http.ServeFile或自定义http.Handler拦截 404 并返回dist/index.html - 构建 Vue 时确保
vue.config.js中publicPath设为"./"(或空字符串),避免硬编码域名或子路径 - 如果 Go 服务监听在子路径如
/app/,Vue 的publicPath必须同步设为"/app/",否则 JS 请求地址错位
用 embed.FS 打包静态资源进二进制,避免部署时漏文件
把 dist/ 目录嵌入二进制最稳妥,不用依赖外部路径、权限或 CDN。但 embed.FS 只接受相对路径字面量,不能拼接变量 —— 所以构建前必须确认目录结构固定。
常见坑是忘记排除开发文件(如 dist/.git、dist/vite.svg)或误把 src/ 一起 embed 进去,导致二进制臃肿甚至 panic。
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 在 Go 文件顶部声明:
//go:embed dist/*,然后var staticFS embed.FS - 用
http.FileServer(http.FS(staticFS))时,注意dist/是嵌入根,所以实际服务的是dist/下的内容;若想根路径直接访问,需http.StripPrefix("/", http.FileServer(http.FS(staticFS)))并确保dist/内有index.html - 调试阶段可先用
os.DirFS("dist")替代embed.FS,验证路径逻辑正确后再切回 embed
处理 SPA 路由 404:自定义 http.Handler 拦截并 fallback
Vue Router 默认用 history 模式,Golang HTTP 服务器不知道 /user/123 应该返回 index.html 而不是 404。不能靠 Nginx 做 fallback —— 因为要求单二进制,所有逻辑必须在 Go 里闭环。
核心思路:先尝试从 FS 读取文件,命中则返回;未命中且路径不含 .(即非资源请求),就返回 index.html。
- 不要用
http.NotFoundHandler全局兜底,它无法区分真实 404 和前端路由 - 推荐写一个闭包 handler:
func spaHandler(fs http.FileSystem) http.Handler { ... },内部用fs.Open(path)检查是否存在,再判断是否为 HTML 路由 - 特别注意:favicon.ico、manifest.json 等也属于静态资源,必须能被正常读取,否则浏览器反复报 404 影响性能
构建流程要串联:Vue 构建完成才触发 Go 编译
Go 二进制打包静态资源的前提是 dist/ 已存在。如果执行 go build 时 dist/ 还没生成,embed 会静默失败(编译通过但运行时报 stat dist: no such file)。
这不是 Go 的 bug,是 embed 的设计限制:它在编译期扫描路径,不支持运行时动态加载。
- CI/CD 或本地发布脚本中,必须严格顺序:
npm run build→ 确认dist/index.html存在 →go build - 可以用 shell 检查:
test -f dist/index.html || (echo "Vue build failed"; exit 1) - 开发时建议加个 Makefile 或
build.sh统一封装,避免手动执行顺序出错
真正麻烦的不是技术点本身,而是 embed 要求构建时环境完全确定 —— 一旦 Vue 输出路径、文件名、哈希策略变动,Go 侧就得同步改 //go:embed 注释和路径处理逻辑。这点容易被忽略,直到上线后白屏才发现 index.html 加载了旧版 JS。

















