上周三凌晨两点,我盯着屏幕上那个诡异的 404 报错,脑子里只剩下一个念头:"这破图片明明在 public 文件夹里躺着,生产环境怎么就找不到了?" 如果你用过 Vite 的静态资源处理,大概率也经历过类似的绝望------开发环境跑得好好的,一上线就崩。今天咱们就挖一挖这个看似简单却暗藏杀机的静态资源加载问题。
1. 场景复现:为什么我的图片在生产环境消失了?
我们的项目是一个中后台管理系统,技术栈是 Vue 3 + Vite,部署在 Nginx 上。需求很简单:在 public/img 下放了一堆静态图标(比如 logo.png),然后在代码里直接引用:
html
<!-- 错误写法 -->
<img src="/img/logo.png" />
开发环境一切正常,但打包部署后,图片死活加载不出来。打开控制台一看,Nginx 返回了 404,请求路径变成了 https://example.com/assets/img/logo.png------路径被自动加上了 /assets 前缀。
2. 根因分析:Vite 的静态资源处理策略
这里的关键在于 Vite 对静态资源的处理分为两种模式:
- 放置在
public目录 :文件会被直接复制到输出目录的根路径,不经过构建流程(适合不改动的文件,比如 favicon.ico)。 - 放置在
src目录 :文件会被视为模块依赖,会经过构建优化(比如压缩、哈希命名)。
问题出在路径解析规则:
- 开发时,Vite Dev Server 会模拟生产环境行为,但默认不处理
public下的文件路径(所以你能看到图片)。 - 生产构建时,如果你配置了
base选项(比如base: '/assets/'),所有资源路径会被自动加上前缀------但public下的文件路径不会自动调整!
3. 解决方案:三选一,各有千秋
方案一:硬编码基础路径(不推荐)
html
<!-- 能跑但很丑 -->
<img src="/assets/img/logo.png" />
缺点:耦合部署环境,换个路径就得改代码。
方案二:动态注入 base(推荐)
在 vite.config.ts 中配置:
typescript
export default defineConfig({
base: process.env.NODE_ENV === 'production' ? '/assets/' : '/'
});
同时用 import.meta.env.BASE_URL 动态拼接路径:
html
<!-- 正确写法 -->
<img :src="`${import.meta.env.BASE_URL}img/logo.png`" />
方案三:放到 src 目录并用 import(最优解)
vue
<script setup>
import logo from '@/assets/img/logo.png'; // 需要配置别名
</script>
<template>
<img :src="logo" /> <!-- 自动处理路径和哈希 -->
</template>
- 性能对比*:
| 方案 | 构建时间 | 请求数 | 缓存命中率 |
|---|---|---|---|
| 硬编码路径 | 最快 | 多 | 低 |
| 动态 base | 中等 | 中等 | 高 |
| import 引入 | 最慢 | 少 | 极高 |
4. 避坑清单:静态资源的其他暗礁
- 哈希缓存失效 : Vite 默认对
src下的资源加哈希,但如果你用?raw或?url导入,会绕过这个机制------可能导致缓存不更新。 - 字体文件加载 : 用
@font-face引用字体时,路径必须写相对于 CSS 文件的位置,而不是项目根目录。 - 多级部署路径 : 如果项目部署在
example.com/sub/path/,必须确保base配置以/结尾(比如base: '/sub/path/'),否则路由和资源路径会错乱。 - SVG 雪碧图陷阱 : 直接引用
public下的 SVG 文件时,某些插件(比如vite-plugin-svg-icons)可能无法正确处理------建议用 import 导入。
5. 终极结论:怎么选?
- *"能用
import就别用public"**------这是我在踩了三次坑后总结的铁律。虽然public用起来简单,但一旦涉及路径调整、缓存优化、按需加载,它就是个定时炸弹。
你在项目里是怎么处理静态资源的?有没有遇到过更诡异的加载问题?评论区等你来战。