[{"data":1,"prerenderedAt":1101},["ShallowReactive",2],{"post-go-embed-frontend":3},{"id":4,"title":5,"author":6,"body":7,"description":1086,"draft":1087,"extension":1088,"image":1089,"meta":1090,"navigation":82,"path":1091,"pinned":1087,"published":1092,"seo":1093,"stem":1094,"tags":1095,"__hash__":1100},"posts\u002Fposts\u002Fgo-embed-frontend\u002Findex.md","Go 单文件打包实战：把前端塞进二进制",null,{"type":8,"value":9,"toc":1069},"minimark",[10,14,23,26,29,40,43,48,458,462,573,577,718,721,725,734,752,763,783,797,803,811,919,925,929,936,945,948,986,989,994,1039,1042,1048,1062,1065],[11,12,13],"h2",{"id":13},"背景",[15,16,17,18,22],"p",{},"一个 Gin + Vue 3 的全栈项目，之前的发布方式是：一个 Go 二进制 + 一个 ",[19,20,21],"code",{},"web\u002F"," 前端静态目录，必须放一起才能运行。",[15,24,25],{},"为了让部署更丝滑——一个 exe 拖哪都能跑——决定把前端构建产物嵌入到 Go 二进制里。",[11,27,28],{"id":28},"核心思路",[15,30,31,32,35,36,39],{},"利用 Go 1.16 引入的 ",[19,33,34],{},"\u002F\u002Fgo:embed"," 指令，在编译时将前端 ",[19,37,38],{},"dist\u002F"," 目录嵌入二进制，运行时通过嵌入的文件系统提供 SPA 服务。",[11,41,42],{"id":42},"逐步实现",[44,45,47],"h3",{"id":46},"_1-创建嵌入模块","1. 创建嵌入模块",[49,50,55],"pre",{"className":51,"code":52,"language":53,"meta":54,"style":54},"language-go shiki shiki-themes github-light github-dark","\u002F\u002F service\u002Fweb\u002Fembed.go\npackage web\n\nimport (\n    \"embed\"\n    \"io\u002Ffs\"\n    \"mime\"\n    \"net\u002Fhttp\"\n    \"path\u002Ffilepath\"\n    \"strings\"\n\n    \"github.com\u002Fgin-gonic\u002Fgin\"\n)\n\n\u002F\u002Fgo:embed dist\nvar embedFS embed.FS\n\nfunc SPAHandler() gin.HandlerFunc {\n    sub, _ := fs.Sub(embedFS, \"dist\")\n\n    return func(c *gin.Context) {\n        p := strings.TrimPrefix(c.Request.URL.Path, \"\u002F\")\n        if p == \"\" {\n            p = \"index.html\"\n        }\n\n        data, err := fs.ReadFile(sub, p)\n        if err != nil {\n            \u002F\u002F SPA fallback：文件不存在则返回 index.html\n            data, _ = fs.ReadFile(sub, \"index.html\")\n        }\n\n        c.Data(http.StatusOK, mime.TypeByExtension(filepath.Ext(p)), data)\n    }\n}\n","go","",[19,56,57,66,77,84,94,107,117,127,137,147,157,162,172,178,183,189,206,211,234,257,262,291,313,330,342,348,353,369,386,392,412,417,422,446,452],{"__ignoreMap":54},[58,59,62],"span",{"class":60,"line":61},"line",1,[58,63,65],{"class":64},"sHbNN","\u002F\u002F service\u002Fweb\u002Fembed.go\n",[58,67,69,73],{"class":60,"line":68},2,[58,70,72],{"class":71},"siTax","package",[58,74,76],{"class":75},"sw2iP"," web\n",[58,78,80],{"class":60,"line":79},3,[58,81,83],{"emptyLinePlaceholder":82},true,"\n",[58,85,87,90],{"class":60,"line":86},4,[58,88,89],{"class":71},"import",[58,91,93],{"class":92},"sIX_F"," (\n",[58,95,97,101,104],{"class":60,"line":96},5,[58,98,100],{"class":99},"scXbn","    \"",[58,102,103],{"class":75},"embed",[58,105,106],{"class":99},"\"\n",[58,108,110,112,115],{"class":60,"line":109},6,[58,111,100],{"class":99},[58,113,114],{"class":75},"io\u002Ffs",[58,116,106],{"class":99},[58,118,120,122,125],{"class":60,"line":119},7,[58,121,100],{"class":99},[58,123,124],{"class":75},"mime",[58,126,106],{"class":99},[58,128,130,132,135],{"class":60,"line":129},8,[58,131,100],{"class":99},[58,133,134],{"class":75},"net\u002Fhttp",[58,136,106],{"class":99},[58,138,140,142,145],{"class":60,"line":139},9,[58,141,100],{"class":99},[58,143,144],{"class":75},"path\u002Ffilepath",[58,146,106],{"class":99},[58,148,150,152,155],{"class":60,"line":149},10,[58,151,100],{"class":99},[58,153,154],{"class":75},"strings",[58,156,106],{"class":99},[58,158,160],{"class":60,"line":159},11,[58,161,83],{"emptyLinePlaceholder":82},[58,163,165,167,170],{"class":60,"line":164},12,[58,166,100],{"class":99},[58,168,169],{"class":75},"github.com\u002Fgin-gonic\u002Fgin",[58,171,106],{"class":99},[58,173,175],{"class":60,"line":174},13,[58,176,177],{"class":92},")\n",[58,179,181],{"class":60,"line":180},14,[58,182,83],{"emptyLinePlaceholder":82},[58,184,186],{"class":60,"line":185},15,[58,187,188],{"class":64},"\u002F\u002Fgo:embed dist\n",[58,190,192,195,198,200,203],{"class":60,"line":191},16,[58,193,194],{"class":71},"var",[58,196,197],{"class":92}," embedFS ",[58,199,103],{"class":75},[58,201,202],{"class":92},".",[58,204,205],{"class":75},"FS\n",[58,207,209],{"class":60,"line":208},17,[58,210,83],{"emptyLinePlaceholder":82},[58,212,214,217,220,223,226,228,231],{"class":60,"line":213},18,[58,215,216],{"class":71},"func",[58,218,219],{"class":75}," SPAHandler",[58,221,222],{"class":92},"() ",[58,224,225],{"class":75},"gin",[58,227,202],{"class":92},[58,229,230],{"class":75},"HandlerFunc",[58,232,233],{"class":92}," {\n",[58,235,237,240,243,246,249,252,255],{"class":60,"line":236},19,[58,238,239],{"class":92},"    sub, _ ",[58,241,242],{"class":71},":=",[58,244,245],{"class":92}," fs.",[58,247,248],{"class":75},"Sub",[58,250,251],{"class":92},"(embedFS, ",[58,253,254],{"class":99},"\"dist\"",[58,256,177],{"class":92},[58,258,260],{"class":60,"line":259},20,[58,261,83],{"emptyLinePlaceholder":82},[58,263,265,268,271,274,278,281,283,285,288],{"class":60,"line":264},21,[58,266,267],{"class":71},"    return",[58,269,270],{"class":71}," func",[58,272,273],{"class":92},"(",[58,275,277],{"class":276},"sPK6S","c",[58,279,280],{"class":71}," *",[58,282,225],{"class":75},[58,284,202],{"class":92},[58,286,287],{"class":75},"Context",[58,289,290],{"class":92},") {\n",[58,292,294,297,299,302,305,308,311],{"class":60,"line":293},22,[58,295,296],{"class":92},"        p ",[58,298,242],{"class":71},[58,300,301],{"class":92}," strings.",[58,303,304],{"class":75},"TrimPrefix",[58,306,307],{"class":92},"(c.Request.URL.Path, ",[58,309,310],{"class":99},"\"\u002F\"",[58,312,177],{"class":92},[58,314,316,319,322,325,328],{"class":60,"line":315},23,[58,317,318],{"class":71},"        if",[58,320,321],{"class":92}," p ",[58,323,324],{"class":71},"==",[58,326,327],{"class":99}," \"\"",[58,329,233],{"class":92},[58,331,333,336,339],{"class":60,"line":332},24,[58,334,335],{"class":92},"            p ",[58,337,338],{"class":71},"=",[58,340,341],{"class":99}," \"index.html\"\n",[58,343,345],{"class":60,"line":344},25,[58,346,347],{"class":92},"        }\n",[58,349,351],{"class":60,"line":350},26,[58,352,83],{"emptyLinePlaceholder":82},[58,354,356,359,361,363,366],{"class":60,"line":355},27,[58,357,358],{"class":92},"        data, err ",[58,360,242],{"class":71},[58,362,245],{"class":92},[58,364,365],{"class":75},"ReadFile",[58,367,368],{"class":92},"(sub, p)\n",[58,370,372,374,377,380,384],{"class":60,"line":371},28,[58,373,318],{"class":71},[58,375,376],{"class":92}," err ",[58,378,379],{"class":71},"!=",[58,381,383],{"class":382},"suQ91"," nil",[58,385,233],{"class":92},[58,387,389],{"class":60,"line":388},29,[58,390,391],{"class":64},"            \u002F\u002F SPA fallback：文件不存在则返回 index.html\n",[58,393,395,398,400,402,404,407,410],{"class":60,"line":394},30,[58,396,397],{"class":92},"            data, _ ",[58,399,338],{"class":71},[58,401,245],{"class":92},[58,403,365],{"class":75},[58,405,406],{"class":92},"(sub, ",[58,408,409],{"class":99},"\"index.html\"",[58,411,177],{"class":92},[58,413,415],{"class":60,"line":414},31,[58,416,347],{"class":92},[58,418,420],{"class":60,"line":419},32,[58,421,83],{"emptyLinePlaceholder":82},[58,423,425,428,431,434,437,440,443],{"class":60,"line":424},33,[58,426,427],{"class":92},"        c.",[58,429,430],{"class":75},"Data",[58,432,433],{"class":92},"(http.StatusOK, mime.",[58,435,436],{"class":75},"TypeByExtension",[58,438,439],{"class":92},"(filepath.",[58,441,442],{"class":75},"Ext",[58,444,445],{"class":92},"(p)), data)\n",[58,447,449],{"class":60,"line":448},34,[58,450,451],{"class":92},"    }\n",[58,453,455],{"class":60,"line":454},35,[58,456,457],{"class":92},"}\n",[44,459,461],{"id":460},"_2-改造路由","2. 改造路由",[49,463,465],{"className":51,"code":464,"language":53,"meta":54,"style":54},"\u002F\u002F router.go\nimport \"myproject\u002Fweb\"\n\n\u002F\u002F 替换原来的磁盘静态文件服务\nwebHandler := web.SPAHandler()\nrouter.GET(\"\u002F\", webHandler)\nrouter.GET(\"\u002Fassets\u002F*filepath\", webHandler)\nrouter.GET(\"\u002Ffavicon.ico\", webHandler)\nrouter.GET(\"\u002Ffavicon.svg\", webHandler)\nrouter.NoRoute(webHandler)\n",[19,466,467,472,484,488,493,509,524,537,550,563],{"__ignoreMap":54},[58,468,469],{"class":60,"line":61},[58,470,471],{"class":64},"\u002F\u002F router.go\n",[58,473,474,476,479,482],{"class":60,"line":68},[58,475,89],{"class":71},[58,477,478],{"class":99}," \"",[58,480,481],{"class":75},"myproject\u002Fweb",[58,483,106],{"class":99},[58,485,486],{"class":60,"line":79},[58,487,83],{"emptyLinePlaceholder":82},[58,489,490],{"class":60,"line":86},[58,491,492],{"class":64},"\u002F\u002F 替换原来的磁盘静态文件服务\n",[58,494,495,498,500,503,506],{"class":60,"line":96},[58,496,497],{"class":92},"webHandler ",[58,499,242],{"class":71},[58,501,502],{"class":92}," web.",[58,504,505],{"class":75},"SPAHandler",[58,507,508],{"class":92},"()\n",[58,510,511,514,517,519,521],{"class":60,"line":109},[58,512,513],{"class":92},"router.",[58,515,516],{"class":75},"GET",[58,518,273],{"class":92},[58,520,310],{"class":99},[58,522,523],{"class":92},", webHandler)\n",[58,525,526,528,530,532,535],{"class":60,"line":119},[58,527,513],{"class":92},[58,529,516],{"class":75},[58,531,273],{"class":92},[58,533,534],{"class":99},"\"\u002Fassets\u002F*filepath\"",[58,536,523],{"class":92},[58,538,539,541,543,545,548],{"class":60,"line":129},[58,540,513],{"class":92},[58,542,516],{"class":75},[58,544,273],{"class":92},[58,546,547],{"class":99},"\"\u002Ffavicon.ico\"",[58,549,523],{"class":92},[58,551,552,554,556,558,561],{"class":60,"line":139},[58,553,513],{"class":92},[58,555,516],{"class":75},[58,557,273],{"class":92},[58,559,560],{"class":99},"\"\u002Ffavicon.svg\"",[58,562,523],{"class":92},[58,564,565,567,570],{"class":60,"line":149},[58,566,513],{"class":92},[58,568,569],{"class":75},"NoRoute",[58,571,572],{"class":92},"(webHandler)\n",[44,574,576],{"id":575},"_3-一键构建脚本","3. 一键构建脚本",[49,578,582],{"className":579,"code":580,"language":581,"meta":54,"style":54},"language-bash shiki shiki-themes github-light github-dark","#!\u002Fbin\u002Fbash\n# build-standalone.sh\n\nset -e\n\n# 1. 构建前端\npnpm install --no-frozen-lockfile\nnpx vite build\n\n# 2. 复制到嵌入目录\nrm -rf service\u002Fweb\u002Fdist\ncp -r dist service\u002Fweb\u002Fdist\n\n# 3. 编译 Go 二进制\ncd service\ngo build -o myapp.exe \\\n    -ldflags=\"-X myproject\u002Fglobal.RUNCODE=release\" \\\n    main.go\n","bash",[19,583,584,589,594,598,606,610,615,626,637,641,646,657,670,674,679,687,703,713],{"__ignoreMap":54},[58,585,586],{"class":60,"line":61},[58,587,588],{"class":64},"#!\u002Fbin\u002Fbash\n",[58,590,591],{"class":60,"line":68},[58,592,593],{"class":64},"# build-standalone.sh\n",[58,595,596],{"class":60,"line":79},[58,597,83],{"emptyLinePlaceholder":82},[58,599,600,603],{"class":60,"line":86},[58,601,602],{"class":382},"set",[58,604,605],{"class":382}," -e\n",[58,607,608],{"class":60,"line":96},[58,609,83],{"emptyLinePlaceholder":82},[58,611,612],{"class":60,"line":109},[58,613,614],{"class":64},"# 1. 构建前端\n",[58,616,617,620,623],{"class":60,"line":119},[58,618,619],{"class":75},"pnpm",[58,621,622],{"class":99}," install",[58,624,625],{"class":382}," --no-frozen-lockfile\n",[58,627,628,631,634],{"class":60,"line":129},[58,629,630],{"class":75},"npx",[58,632,633],{"class":99}," vite",[58,635,636],{"class":99}," build\n",[58,638,639],{"class":60,"line":139},[58,640,83],{"emptyLinePlaceholder":82},[58,642,643],{"class":60,"line":149},[58,644,645],{"class":64},"# 2. 复制到嵌入目录\n",[58,647,648,651,654],{"class":60,"line":159},[58,649,650],{"class":75},"rm",[58,652,653],{"class":382}," -rf",[58,655,656],{"class":99}," service\u002Fweb\u002Fdist\n",[58,658,659,662,665,668],{"class":60,"line":164},[58,660,661],{"class":75},"cp",[58,663,664],{"class":382}," -r",[58,666,667],{"class":99}," dist",[58,669,656],{"class":99},[58,671,672],{"class":60,"line":174},[58,673,83],{"emptyLinePlaceholder":82},[58,675,676],{"class":60,"line":180},[58,677,678],{"class":64},"# 3. 编译 Go 二进制\n",[58,680,681,684],{"class":60,"line":185},[58,682,683],{"class":382},"cd",[58,685,686],{"class":99}," service\n",[58,688,689,691,694,697,700],{"class":60,"line":191},[58,690,53],{"class":75},[58,692,693],{"class":99}," build",[58,695,696],{"class":382}," -o",[58,698,699],{"class":99}," myapp.exe",[58,701,702],{"class":382}," \\\n",[58,704,705,708,711],{"class":60,"line":208},[58,706,707],{"class":382},"    -ldflags=",[58,709,710],{"class":99},"\"-X myproject\u002Fglobal.RUNCODE=release\"",[58,712,702],{"class":382},[58,714,715],{"class":60,"line":213},[58,716,717],{"class":99},"    main.go\n",[11,719,720],{"id":720},"踩坑记录",[44,722,724],{"id":723},"坑一路由不匹配静态资源-404","坑一：路由不匹配，静态资源 404",[15,726,727,728,730,731,733],{},"首次实现时用了 Gin 的 ",[19,729,569],{}," 兜底，但部分静态资源返回 404。排查发现 ",[19,732,569],{}," 在某些路由未显式注册时不会按预期触发。",[15,735,736,740,741,744,745,748,749,751],{},[737,738,739],"strong",{},"解决","：显式注册 ",[19,742,743],{},"\u002Fassets\u002F*filepath","、",[19,746,747],{},"\u002Ffavicon.ico"," 等路由，",[19,750,569],{}," 仅作为兜底。",[44,753,755,756,758,759,762],{"id":754},"坑二goembed-排除-_-开头文件","坑二：",[19,757,34],{}," 排除 ",[19,760,761],{},"_"," 开头文件",[15,764,765,766,769,770,772,773,782],{},"这是最坑的。Vite 构建产出了一个 ",[19,767,768],{},"_plugin-vue_export-helper-xxx.js","，Go 的 ",[19,771,34],{}," 当模式为目录时，",[737,774,775,776,778,779,781],{},"硬编码排除了 ",[19,777,761],{}," 和 ",[19,780,202],{}," 开头的文件","。",[49,784,786],{"className":51,"code":785,"language":53,"meta":54,"style":54},"\u002F\u002F 这行会跳过 dist\u002Fassets\u002F_plugin-xxx.js\n\u002F\u002Fgo:embed dist\n",[19,787,788,793],{"__ignoreMap":54},[58,789,790],{"class":60,"line":61},[58,791,792],{"class":64},"\u002F\u002F 这行会跳过 dist\u002Fassets\u002F_plugin-xxx.js\n",[58,794,795],{"class":60,"line":68},[58,796,188],{"class":64},[15,798,799,800,782],{},"错误表现：JS 文件返回的是 HTML 内容（SPA fallback），浏览器报 ",[19,801,802],{},"Unexpected token '\u003C'",[15,804,805,807,808,810],{},[737,806,739],{},"：改 Vite 配置，去除了 ",[19,809,761],{}," 前缀：",[49,812,816],{"className":813,"code":814,"language":815,"meta":54,"style":54},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F vite.config.ts\nbuild: {\n  rollupOptions: {\n    output: {\n      chunkFileNames(chunkInfo) {\n        return `assets\u002F${chunkInfo.name.replace(\u002F^_\u002F, '')}-[hash].js`\n      },\n    },\n  },\n},\n","ts",[19,817,818,823,831,838,845,853,899,904,909,914],{"__ignoreMap":54},[58,819,820],{"class":60,"line":61},[58,821,822],{"class":64},"\u002F\u002F vite.config.ts\n",[58,824,825,828],{"class":60,"line":68},[58,826,827],{"class":75},"build",[58,829,830],{"class":92},": {\n",[58,832,833,836],{"class":60,"line":79},[58,834,835],{"class":75},"  rollupOptions",[58,837,830],{"class":92},[58,839,840,843],{"class":60,"line":86},[58,841,842],{"class":75},"    output",[58,844,830],{"class":92},[58,846,847,850],{"class":60,"line":96},[58,848,849],{"class":75},"      chunkFileNames",[58,851,852],{"class":92},"(chunkInfo) {\n",[58,854,855,858,861,864,866,869,871,874,876,879,882,885,887,890,893,896],{"class":60,"line":109},[58,856,857],{"class":71},"        return",[58,859,860],{"class":99}," `assets\u002F${",[58,862,863],{"class":92},"chunkInfo",[58,865,202],{"class":99},[58,867,868],{"class":92},"name",[58,870,202],{"class":99},[58,872,873],{"class":75},"replace",[58,875,273],{"class":99},[58,877,878],{"class":99},"\u002F",[58,880,881],{"class":71},"^",[58,883,761],{"class":884},"sPKmS",[58,886,878],{"class":99},[58,888,889],{"class":99},", ",[58,891,892],{"class":99},"''",[58,894,895],{"class":99},")",[58,897,898],{"class":99},"}-[hash].js`\n",[58,900,901],{"class":60,"line":119},[58,902,903],{"class":92},"      },\n",[58,905,906],{"class":60,"line":129},[58,907,908],{"class":92},"    },\n",[58,910,911],{"class":60,"line":139},[58,912,913],{"class":92},"  },\n",[58,915,916],{"class":60,"line":149},[58,917,918],{"class":92},"},\n",[15,920,921,922,924],{},"这样就避免产生 ",[19,923,761],{}," 前缀文件名的同时，保持构建产物的一致性和引用关系的正确性。",[44,926,928],{"id":927},"坑三构建缓存导致-embed-内容未更新","坑三：构建缓存导致 embed 内容未更新",[15,930,931,932,935],{},"多次 ",[19,933,934],{},"go build"," 后，Go 的构建缓存可能使用了旧版本的 embed 内容，导致实际运行的文件不是最新的。",[15,937,938,940,941,944],{},[737,939,739],{},"：使用 ",[19,942,943],{},"go build -a"," 强制重新编译所有包。",[11,946,947],{"id":947},"最终效果",[49,949,951],{"className":579,"code":950,"language":581,"meta":54,"style":54},"$ bash build-standalone.sh\n=== 构建成功 ===\n输出: service\u002Fmyapp.exe  41M\n",[19,952,953,964,975],{"__ignoreMap":54},[58,954,955,958,961],{"class":60,"line":61},[58,956,957],{"class":75},"$",[58,959,960],{"class":99}," bash",[58,962,963],{"class":99}," build-standalone.sh\n",[58,965,966,969,972],{"class":60,"line":68},[58,967,968],{"class":99},"===",[58,970,971],{"class":99}," 构建成功",[58,973,974],{"class":99}," ===\n",[58,976,977,980,983],{"class":60,"line":79},[58,978,979],{"class":75},"输出:",[58,981,982],{"class":99}," service\u002Fmyapp.exe",[58,984,985],{"class":99},"  41M\n",[15,987,988],{},"一个 41MB 的 exe，内置前端页面、API 后端、SQLite 数据库（运行时初始化），拖到哪都能跑。",[15,990,991],{},[737,992,993],{},"部署对比：",[995,996,997,1013],"table",{},[998,999,1000],"thead",{},[1001,1002,1003,1007,1010],"tr",{},[1004,1005,1006],"th",{},"方式",[1004,1008,1009],{},"文件数",[1004,1011,1012],{},"操作",[1014,1015,1016,1028],"tbody",{},[1001,1017,1018,1022,1025],{},[1019,1020,1021],"td",{},"旧方案",[1019,1023,1024],{},"1 exe + 1 web 目录",[1019,1026,1027],{},"解压→保持目录结构",[1001,1029,1030,1033,1036],{},[1019,1031,1032],{},"新方案",[1019,1034,1035],{},"1 个 exe",[1019,1037,1038],{},"直接扔到服务器运行",[11,1040,1041],{"id":1041},"总结",[15,1043,1044,1045,1047],{},"Go 的 ",[19,1046,34],{}," 是一个强大的工具，但有两个隐藏行为需要注意：",[1049,1050,1051,1059],"ol",{},[1052,1053,1054,1055,778,1057,781],"li",{},"嵌入目录时自动排除 ",[19,1056,761],{},[19,1058,202],{},[1052,1060,1061],{},"构建缓存可能导致 embed 不是最新的",[15,1063,1064],{},"对于前端+后端合体的场景，这是性价比最高的方案——不需要额外的打包工具，纯标准库搞定。",[1066,1067,1068],"style",{},"html pre.shiki code .sHbNN, html code.shiki .sHbNN{--shiki-light:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .siTax, html code.shiki .siTax{--shiki-light:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sw2iP, html code.shiki .sw2iP{--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sIX_F, html code.shiki .sIX_F{--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .scXbn, html code.shiki .scXbn{--shiki-light:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sPK6S, html code.shiki .sPK6S{--shiki-light:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .suQ91, html code.shiki .suQ91{--shiki-light:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sPKmS, html code.shiki .sPKmS{--shiki-light:#032F62;--shiki-dark:#DBEDFF}",{"title":54,"searchDepth":68,"depth":79,"links":1070},[1071,1072,1073,1078,1084,1085],{"id":13,"depth":68,"text":13},{"id":28,"depth":68,"text":28},{"id":42,"depth":68,"text":42,"children":1074},[1075,1076,1077],{"id":46,"depth":79,"text":47},{"id":460,"depth":79,"text":461},{"id":575,"depth":79,"text":576},{"id":720,"depth":68,"text":720,"children":1079},[1080,1081,1083],{"id":723,"depth":79,"text":724},{"id":754,"depth":79,"text":1082},"坑二：\u002F\u002Fgo:embed 排除 _ 开头文件",{"id":927,"depth":79,"text":928},{"id":947,"depth":68,"text":947},{"id":1041,"depth":68,"text":1041},"记录将 Vue 3 前端嵌入 Go 二进制实现单 exe 发布的完整流程，以及遇到的坑和解决方案。",false,"md","\u002Fposts\u002Fgo-embed-frontend\u002Fimg\u002Fcover.jpg",{},"\u002Fposts\u002Fgo-embed-frontend","2026-06-06T16:00:00",{"title":5,"description":1086},"posts\u002Fgo-embed-frontend\u002Findex",[1096,1097,1098,1099],"Go","Vue","编译","埋坑","aIlmmahxbuSBfpkrqwsdrbCQWjg7QHGt8tTc4iVnb5Q",1780733791991]