资源管理
Nuxt 中有两种资源目录:app/assets/ 和 public/,它们的处理方式截然不同。理解两者区别,才能正确组织项目文件。
assets/ vs public/ ——核心区别
| 特性 | app/assets/ | public/ |
|---|---|---|
| 构建处理 | ✅ Vite 处理(压缩、哈希) | ❌ 原样复制到输出目录 |
| 文件名哈希 | ✅ 内容哈希(logo.a1b2c3.png) | ❌ 保持原名 |
| Tree-shaking | ✅ 未引用不打包 | ❌ 全部复制 |
| 引用方式 | ~/assets/... | /...(绝对路径) |
| 版本控制 | ✅ 哈希自动更新缓存 | ❌ 手动管理缓存 |
| 适用 | CSS、需要优化的图片、SVG | favicon、robots.txt |
一句话总结
assets/ 是"需要处理的资源",public/ 是"原样使用的资源"。
选择原则
- 需要优化/哈希/Tree-shaking →
assets/ - 必须保持固定路径 →
public/
assets/ 目录
存放需要构建工具处理的资源,会被 Vite 处理(压缩、哈希、Tree-shaking)。
引用方式
vue
<template>
<!-- 模板中:使用 ~/ 别名 -->
<img src="~/assets/images/logo.png" alt="Logo" />
</template>
<style scoped>
/* CSS 中:使用 ~/ 别名 */
.hero {
background-image: url('~/assets/images/hero-bg.jpg');
}
</style>
<script setup>
// JS 中:使用 ~/ 或 @/ 别名
const logoUrl = '@/assets/images/logo.png'
</script>路径别名说明
| 别名 | 指向 | 适用场景 |
|---|---|---|
~/ | 项目根目录 | 推荐,最常用 |
@/ | 项目根目录 | 与 ~/ 等价 |
~~/ | 项目根目录 | Nuxt 4 中与 ~/ 相同 |
#imports | 自动导入目录 | 用于显式导入自动导入的模块 |
适合放 assets/ 的文件
| 文件类型 | 原因 | 示例 |
|---|---|---|
| CSS / SCSS | 需要编译处理 | main.css、_variables.scss |
| 需要优化的图片 | 需要压缩、哈希 | 产品图片、背景图 |
| SVG 图标 | 需要 Tree-shaking | 图标文件 |
| 字体文件 | 需要哈希避免缓存问题 | .woff2 字体 |
assets 的构建处理流程
text
源码中引用 ~/assets/images/logo.png
↓
Vite 构建时处理
↓
1. 如果是图片:压缩 → 添加内容哈希 → 输出为 logo.a1b2c3.png
2. 如果是 CSS:编译 → 添加内容哈希 → 输出为 main.d4e5f6.css
3. 如果是 SVG:可以内联或作为文件引用
↓
HTML/CSS 中引用路径自动替换为构建后的路径内容哈希的好处
文件内容变化时哈希会变,浏览器会自动获取新版本。内容不变时哈希不变,浏览器使用缓存。这是长期缓存策略的基础。
public/ 目录
存放不需要处理的静态资源,构建时原样复制到输出目录。
引用方式
vue
<template>
<!-- 绝对路径,不需要别名 -->
<img src="/images/og-image.png" alt="OG" />
<link rel="icon" href="/favicon.svg" />
</template>适合放 public/ 的文件
| 文件类型 | 原因 | 示例 |
|---|---|---|
| favicon | 必须在根路径可访问 | favicon.ico、favicon.svg |
| robots.txt | 搜索引擎需要固定路径 | robots.txt |
| sitemap.xml | 搜索引擎需要固定路径 | sitemap.xml |
| 社交分享图片 | Open Graph 需要绝对 URL | og-image.png |
| 不需要处理的字体 | 已优化好的字体 | 已子集化的字体 |
| manifest.json | PWA 需要 | manifest.json |
INFO
️ public/ 中的文件会全部复制到输出目录 即使没有被引用。不要把大量无用文件放在 public/ 中,会增大部署包体积
动态图片引用
动态引用 assets 中的图片需要特殊处理,因为 Vite 需要在构建时分析路径:
方式 1:new URL(推荐)
vue
<script setup>
const props = defineProps<{ name: string }>()
// ✅ 使用 new URL——Vite 能正确分析
const getImageUrl = (name: string) => {
return new URL(`../assets/images/${name}.png`, import.meta.url).href
}
</script>
<template>
<img :src="getImageUrl(name)" :alt="name" />
</template>new URL 的原理
import.meta.url 是当前模块的 URL,Vite 在构建时会将相对路径解析为正确的资源路径。这是 Vite 官方推荐的动态导入方式。
方式 2:import.meta.glob
vue
<script setup>
// 一次性导入所有图片
const images = import.meta.glob('~/assets/images/*.png', {
eager: true,
import: 'default',
})
// 使用
const getImageUrl = (name: string) => {
return images[`/assets/images/${name}.png`]
}
</script>import.meta.glob 的优缺点
- 优点:一次性处理所有图片,代码简洁
- 缺点:所有图片都会被打包(即使没用到),不适合大量图片
适用场景
图片数量少(< 20)且大多数都会用到的情况。
方式 3:静态映射
vue
<script setup>
// 图片数量少时,手动映射最安全
const imageMap: Record<string, string> = {
logo: '~/assets/images/logo.png',
hero: '~/assets/images/hero.png',
banner: '~/assets/images/banner.png',
}
const props = defineProps<{ type: string }>()
const imgSrc = computed(() => imageMap[props.type])
</script>方式 4:放在 public/ 目录
vue
<script setup>
// 如果图片在 public/ 中,直接拼接路径
const imgSrc = computed(() => `/images/${name}.png`)
</script>INFO
️ 放在 public/ 的缺点:不会经过 Vite 优化(无哈希、无压缩) 不适合大量图片
服务端静态资源
在 server/ 目录中引用静态资源:
ts
// server/api/download.ts
export default defineEventHandler((event) => {
// 读取 public/ 中的文件
const filePath = `${process.cwd()}/public/files/document.pdf`
return sendStream(event, createReadStream(filePath))
})服务端不能使用 ~/assets/ 路径
assets 目录是 Vite 的概念,只在客户端构建时存在。服务端只能访问 public/ 目录或文件系统中的文件。
用户上传文件
用户上传的文件不应放在 public/ 或 assets/,因为:
- 这些目录在构建时处理,运行时写入的文件不会自动生效
- 服务器重启/重新部署后文件会丢失
推荐方案
ts
// server/api/upload.ts
export default defineEventHandler(async (event) => {
const files = await readMultipartFormData(event)
// 方案 1:使用对象存储(推荐生产环境)
// await uploadToOSS(file)
// 方案 2:使用 Nitro 存储层
await useStorage('uploads').setItem(file.filename, file.data)
// 方案 3:写入服务端临时目录(开发用)
// const filePath = path.join('/tmp', file.filename)
// await fs.writeFile(filePath, file.data)
})生产环境推荐对象存储
(如阿里云 OSS、AWS S3),具有高可用、CDN 加速、权限控制等优势。
资源引用常见问题
1. 为什么图片不显示?
vue
<!-- ❌ 错误:动态路径不会被 Vite 处理 -->
<img :src="`~/assets/images/${name}.png`" />
<!-- ✅ 正确:使用 new URL 或放在 public/ -->
<img :src="getImageUrl(name)" />2. CSS 中的背景图不生效?
css
/* ❌ 错误:路径不正确 */
.hero {
background-image: url('assets/images/hero.jpg');
}
/* ✅ 正确:使用 ~/ 别名 */
.hero {
background-image: url('~/assets/images/hero.jpg');
}3. SVG 如何使用?
vue
<!-- 方式 1:作为图片(不能修改颜色) -->
<img src="~/assets/icons/logo.svg" alt="Logo" />
<!-- 方式 2:内联 SVG(可以修改颜色,推荐) -->
<!-- 安装 vite-svg-loader 或使用 @nuxt/icon -->
<Icon name="logo" />
<!-- 方式 3:CSS 背景图 -->
<style scoped>
.icon {
background-image: url('~/assets/icons/logo.svg');
}
</style>知识脉络
text
CSS 与 SCSS → 你在这里:资源管理
│
├─→ 下一步:图片优化
│
└─→ 相关:目录结构 → public 目录