开发 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
参考资料
- Vite 官方文档 - 静态资源处理 - 详细说明了 public 目录和 import 的区别
- Vite 官方文档 - public 目录 - public 目录的使用规则
- React 官方文档 - Adding Images, Fonts, and Files - React 项目中静态资源的处理方式
写在最后
研究完这个问题,我的理解是:Vite 的资源处理分两套系统------构建系统 (src/ 目录)和静态服务(public/ 目录),用错了系统就会 404。
下次添加新图标时,记住这个决策树:
- 图标数量多、需要动态路径 →
public/images/icons/ - 单个图标、紧密耦合组件 →
src/assets/+import - 需要修改 SVG 属性(颜色、大小) →
import+ SVGR 插件
如果你还没规划项目的静态资源目录结构,建议现在就去整理一下。清晰的目录结构能避免 90% 的资源 404 问题------别成为那个被 404 困扰一下午的倒霉蛋。