TL;DR:Vue3 + Vite 项目本地
npm run dev一切正常、build后部署到 GitHub Pages / Gitee Pages / Nginx 子路径就地图白屏、资源 404------先看 Network 里 404 的 URL 缺没缺子路径前缀,缺了就是base与静态路径问题。排查顺序和正确写法见下,文末 Demo 可一键切换错误 / 正确两种状态。
上个月交付一个水利一张图,本地跑得好好的,推到客户内网 Nginx 的 /gis/ 子路径下,打开直接白屏。F12 一看,/assets/index-xxx.js 全部 404------ 资源引用写死在根路径,部署到子路径当然找不到。
这种坑的典型特征就是本地测不出来、一部署就炸,而且 404 的不是业务代码,是打包产物的资源引用。这篇把排查顺序和根治写法一次讲清,本机 Chrome 实测,2026-10。
先看 404 的 URL 长什么样
打开 F12 Network,过滤 404,盯着失败的请求看两秒:
-
https://host/gis/assets/index-xxx.js→ 带上了子路径,问题在资源本身不存在或路径写错; -
https://host/assets/index-xxx.js→ 缺了/gis/前缀,就是 base / 静态路径问题,直接往下走。
这一步决定了排查方向,别一上来就翻配置文件。90% 的 "部署后白屏" 是第二种形态。
Vite 默认 base: '/',产物引用写死根路径
Vite 默认把资源引用写成绝对根路径,build 后打开 dist/index.html 能看到:
javascript
<script type="module" src="/assets/index-BCH-ek3h.js"></script>
部署到 https://host/gis/ 后,浏览器按规则把 /assets/... 拼成 https://host/assets/...------ 子路径 gis 被丢掉了,404。这不是你代码的问题,是构建配置没适配部署位置。
正确做法,二选一:
typescript
`vite.config.ts`
export default defineConfig({
// 方案一:相对路径,任何子路径都能部署
base: './',
})
typescript
`vite.config.ts`
// 方案二:明确指定部署子路径(部署位置固定时更稳)
export default defineConfig({
base: '/gis/',
})
base: './' 会把产物引用改成相对路径(./assets/index-xxx.js),相对 index.html 所在目录解析,部署到哪层子路径都不会丢。这个 demo 工程就是用 hash 路由 + 相对 base 部署的,Gitee Pages 的 /仓库名/ 子路径下直接能跑。
代码里写死的根绝对路径,grep 一下就有数
base 改完了,再 grep 一下源码里有没有手写根路径:
bash
grep -rn "src=\"/" src/ # 模板里的图片资源
grep -rn "new URL('/" src/ # 动态 URL
grep -rn "fetch('/" src/ # 接口 / 静态文件请求
常见三处:<img src="/assets/xxx">、fetch('/api/...')、new URL('/xxx', import.meta.url)。这些路径不受 base 影响,部署到子路径后照样 404。地图相关的静态资源(瓦片、GeoJSON、模型)要用构建期变量拼:
vue
<script setup lang="ts">
// BASE_URL 由 base 配置注入,部署到 /gis/ 时它是 '/gis/'
const tileUrl = import.meta.env.BASE_URL + 'data/tiles/{z}/{x}/{y}.png'
</script>
<template>
<img :src="import.meta.env.BASE_URL + 'img/底图示意.png'" alt="底图资源" />
</template>
BASE_URL 是 Vite 构建时注入的部署根路径变量,跟着 base 走 ------base 是 /gis/ 它就是 /gis/,base 是 ./ 它就是相对值。所有 "会变成 URL 的静态资源" 统一走它,不再手写。
public 目录资源没拼前缀
public/ 下的文件会原样拷进 dist/ 根目录。很多人写 <img src="/logo.png"> 本地没事,部署到子路径一样 404。public 资源的引用规则是:
javascript
<!-- 错误:手写根路径 -->
<img src="/img/logo.png" />
<!-- 正确:BASE_URL + public 相对路径 -->
<img :src="import.meta.env.BASE_URL + 'img/logo.png'" />
demo 里我模拟了完整场景:资源放在 public/docs/assets/(相当于部署在 /docs/ 子路径),错误写法请求 /assets/w5-tile.png(缺前缀,加载失败),正确写法请求 /docs/assets/w5-tile.png(命中,正常显示)------ 按钮一键切换,打开就能对比。


隐藏坑:本地 dev 的 SPA fallback 会掩盖 404
这是最容易误判的一环。本地 npm run dev 时,Vite dev server 对未命中的路径会返回 index.html(SPA fallback)------ 你写错路径,浏览器拿到的不是 404,而是一段 HTML。图片拿到 HTML 会解码失败照样裂开,JS 引用拿到 HTML 照样白屏,但 Network 面板里看不到 404,看着像 "资源在但没用上",把排查方向带偏。
所以验证要绕开 dev:
-
npm run build后,直接在dist/里双击index.html看效果(相对 base 时本地文件协议也能跑); -
或者
npm run preview,它就是模拟生产环境的产物服务; -
部署后再看一次 Network,这时候的 404 才是真的。
另外提醒一句:只改 base 不够。base 只管构建产物的引用,你代码里手写的绝对路径 (原因二)和 public 资源引用(原因三)都得跟着改,缺一个都会在部署环境露出 404。改完用上面的 grep 三连过一遍最省事。
小结
按这个清单过一遍,部署后资源 404 基本能一次修完:
-
Network 里看 404 URL 缺没缺子路径前缀 → 判定是不是 base 问题;
-
vite.config.ts配base: './'或具体子路径; -
grep 源码里写死的
/assets、/img、fetch('/,改用import.meta.env.BASE_URL拼接; -
public 资源引用同样走
BASE_URL; -
别在 dev 里验证,用
dist双击或preview复现真实部署行为。
路径这关过了,再遇到白屏就回到渲染本身去查 ------ 容器、初始化时机、底图源,那是另一条线。
📦 Demo 见 jgwx-notes-code 工程 /vite-asset-404 路由(npm run dev 后浏览器直接打开,错误 / 正确一键切换即可复现)。