Vite 中 SVG 404 的幕后黑手:你真的懂静态资源处理吗?

开发 React 项目的时候,遇到个挺典型的问题:页面上的 SVG 图标全部显示不出来,控制台刷刷刷报一堆 404 错误。

看了下错误信息,浏览器在请求这些路径:

  • foundry-cost-icon.svg:1 Failed to load resource: the server responded with a status of 404 ()
  • foundry-roi-icon.svg:1 Failed to load resource: the server responded with a status of 404 ()
  • foundry-deploy-icon.svg:1 Failed to load resource: the server responded with a status of 404 ()
  • foundry-update-icon.svg:1 Failed to load resource: the server responded with a status of 404 ()

页面上"为何选择 Foundry"那个区块的 4 个图标,全都加载失败了。

问题根源:误用源码路径作为资源 URL

翻了下代码,发现问题在这里:

tsx 复制代码
// FoundryPage.tsx (错误写法)
<img
  src="/src/assets/icons/foundry/foundry-roi-icon.svg"
  alt="ROI icon"
  className="w-full h-full"
/>

乍一看没问题啊,文件确实在 src/assets/icons/foundry/ 目录下:

bash 复制代码
$ ls -la src/assets/icons/foundry/
total 32
-rw-r--r--@ 1 tsk  staff   373 Nov  4 18:28 foundry-cost-icon.svg
-rw-r--r--@ 1 tsk  staff  1870 Nov  4 18:28 foundry-deploy-icon.svg
-rw-r--r--@ 1 tsk  staff   955 Nov  4 18:28 foundry-roi-icon.svg
-rw-r--r--@ 1 tsk  staff   812 Nov  4 18:28 foundry-update-icon.svg

但这就是问题所在------我把源码路径当成了浏览器访问的 URL 路径

Vite 的资源处理机制

想明白这个问题,得先理解 Vite 怎么处理静态资源的:

方式 1:import 导入(推荐用于组件内)

tsx 复制代码
import logoSvg from '@/assets/logo.svg';

function Header() {
  return <img src={logoSvg} alt="Logo" />;
}

工作原理

  • Vite 会把 SVG 文件当作模块处理
  • 构建时自动生成带 hash 的文件名(如 logo-a3f2b9c1.svg
  • logoSvg 变量保存的是构建后的实际路径

优点

  • 自动 hash,利于缓存
  • TypeScript 类型支持
  • 构建时优化(压缩、转换)

缺点

  • 每个图标都要写一行 import
  • 动态路径不好处理

方式 2:放在 public 目录(推荐用于公共资源)

tsx 复制代码
// 文件位置:public/images/icons/arrow.svg
function Icon() {
  return <img src="/images/icons/arrow.svg" alt="Arrow" />;
}

工作原理

  • public/ 目录下的文件会被原样复制到构建输出目录
  • URL 路径直接对应文件路径
  • 没有任何处理和转换

优点

  • 不需要 import
  • 路径可以动态拼接
  • 适合大量图标/静态文件

缺点

  • 没有文件 hash(缓存管理靠手动版本号)
  • 不会被构建优化
  • 文件引用错误在构建时检测不到(只能运行时 404)

方式 3:错误示范(就是我踩的坑)

tsx 复制代码
// ❌ 错误:把源码路径当 URL
<img src="/src/assets/icons/foundry/foundry-roi-icon.svg" />

为什么会 404

  • 浏览器会向服务器请求 http://localhost:5173/src/assets/icons/foundry/foundry-roi-icon.svg
  • 但这个路径在开发服务器的静态资源映射中不存在
  • src/ 目录下的文件必须通过 import 引入,不能直接作为 URL 访问

解决方案:迁移到 public 目录

既然项目中有很多图标,而且路径需要动态拼接(比如循环渲染),最适合的方式是用 public/ 目录。

步骤 1:移动文件

bash 复制代码
# 创建目标目录
mkdir -p public/images/icons/foundry

# 复制图标文件
cp src/assets/icons/foundry/*.svg public/images/icons/foundry/

# 验证文件已复制
ls -la public/images/icons/foundry/

输出:

diff 复制代码
total 32
-rw-r--r--@ 1 tsk  staff   373B Nov  5 09:44 foundry-cost-icon.svg
-rw-r--r--@ 1 tsk  staff   1.8K Nov  5 09:44 foundry-deploy-icon.svg
-rw-r--r--@ 1 tsk  staff   955B Nov  5 09:44 foundry-roi-icon.svg
-rw-r--r--@ 1 tsk  staff   812B Nov  5 09:44 foundry-update-icon.svg

步骤 2:更新代码中的路径

修改 FoundryPage.tsx,把所有图标路径从 /src/assets/... 改为 /images/...

tsx 复制代码
// Before
<img
  src="/src/assets/icons/foundry/foundry-roi-icon.svg"
  alt="ROI icon"
  className="w-full h-full"
/>

// After ✅
<img
  src="/images/icons/foundry/foundry-roi-icon.svg"
  alt="ROI icon"
  className="w-full h-full"
/>

步骤 3:清理旧文件

确认没有其他地方引用这些文件后,删除旧目录:

bash 复制代码
# 检查是否还有其他引用
grep -r "src/assets/icons/foundry" src/ --include="*.tsx" --include="*.ts"
# (没有输出,说明安全)

# 删除旧目录
rm -rf src/assets/icons/foundry

验证修复

刷新浏览器,打开开发者工具:

修复前

ruby 复制代码
❌ GET http://localhost:5173/src/assets/icons/foundry/foundry-roi-icon.svg 404 (Not Found)
❌ GET http://localhost:5173/src/assets/icons/foundry/foundry-cost-icon.svg 404 (Not Found)
...

修复后

ruby 复制代码
✅ GET http://localhost:5173/images/icons/foundry/foundry-roi-icon.svg 200 (OK)
✅ GET http://localhost:5173/images/icons/foundry/foundry-cost-icon.svg 200 (OK)
...

页面上的 4 个图标全部正常显示,404 错误消失。


最佳实践建议

根据这次踩坑经验,总结几个建议:

1. 静态资源目录规划

建议项目初期就规划好目录结构:

csharp 复制代码
项目根目录/
├── public/              # 不需要构建处理的静态资源
│   ├── images/          # 图片资源
│   │   ├── icons/       # 图标(SVG/PNG)
│   │   ├── backgrounds/ # 背景图
│   │   └── logos/       # Logo
│   ├── fonts/           # 字体文件
│   └── data/            # 静态 JSON 数据
│
├── src/
│   ├── assets/          # 需要构建处理的资源
│   │   ├── styles/      # 样式文件
│   │   └── images/      # 组件专用图片(会被 import)
│   └── components/

决策规则

  • public/:大量通用资源(图标库、公共图片)、动态路径拼接的资源
  • src/assets/:组件紧密相关的资源、需要构建优化的资源

2. 图标引用方式选择

场景 推荐方式 示例
单个图标,固定引用 import + src/assets import logo from '@/assets/logo.svg'
多个图标,循环渲染 public 目录 + 字符串路径 <img src="/images/icons/${name}.svg" />
Icon 组件库 import + 动态导入 const Icon = lazy(() => import(...))
需要 SVG 内联(修改颜色等) import + SVGR 插件 import { ReactComponent as Logo } from './logo.svg'

3. 避免常见错误

错误 1:混淆源码路径和 URL 路径

tsx 复制代码
❌ <img src="/src/components/Icon.svg" />
✅ <img src="/images/icons/icon.svg" />  // public 目录
✅ import icon from '@/components/Icon.svg'  // src 目录

错误 2:public 文件路径写错

tsx 复制代码
// 文件位置:public/images/logo.png

❌ <img src="images/logo.png" />        // 缺少开头的 /
❌ <img src="/public/images/logo.png" />  // 多了 /public
✅ <img src="/images/logo.png" />

记住 :public 目录下的文件,URL 路径要去掉 public/ 前缀 ,并且开头加 /


错误 3:构建后路径失效

tsx 复制代码
// 开发环境正常,生产环境 404
<img src="./images/logo.png" />  // ❌ 相对路径在路由变化时会出问题

✅ <img src="/images/logo.png" />  // 绝对路径

项目目录结构(修复后)

css 复制代码
zhiman-fe/
├── public/
│   └── images/
│       └── icons/
│           ├── foundry/              # 新增:Foundry 图标
│           │   ├── foundry-cost-icon.svg
│           │   ├── foundry-deploy-icon.svg
│           │   ├── foundry-roi-icon.svg
│           │   └── foundry-update-icon.svg
│           └── arrow-right.svg       # 已有的其他图标
│
├── src/
│   ├── assets/
│   │   └── icons/
│   │       ├── direction-top.svg     # 组件专用图标(保留)
│   │       └── foundry/              # 已删除(迁移到 public)
│   └── pages/
│       └── FoundryPage.tsx           # 已更新图标路径

总结

这次踩坑让我重新理解了 Vite 的资源处理机制:

核心原则

  • src/ 目录 = 需要构建处理的源码,必须通过 import 引入
  • public/ 目录 = 静态资源,直接通过 URL 访问(路径去掉 public/ 前缀)
  • 永远不要 把源码路径(/src/...)当作浏览器访问的 URL

参考资料

  1. Vite 官方文档 - 静态资源处理 - 详细说明了 public 目录和 import 的区别
  2. Vite 官方文档 - public 目录 - public 目录的使用规则
  3. React 官方文档 - Adding Images, Fonts, and Files - React 项目中静态资源的处理方式

写在最后

研究完这个问题,我的理解是:Vite 的资源处理分两套系统------构建系统 (src/ 目录)和静态服务(public/ 目录),用错了系统就会 404。

下次添加新图标时,记住这个决策树:

  • 图标数量多、需要动态路径 → public/images/icons/
  • 单个图标、紧密耦合组件 → src/assets/ + import
  • 需要修改 SVG 属性(颜色、大小) → import + SVGR 插件

如果你还没规划项目的静态资源目录结构,建议现在就去整理一下。清晰的目录结构能避免 90% 的资源 404 问题------别成为那个被 404 困扰一下午的倒霉蛋。

相关推荐
weixin_382395239 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__9 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.9 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~10 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华11 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子13 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅13 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni14 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学15 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端