首页 / Nuxt 4 入门教程 / 静态资源 assets 与 public

Nuxt 4 入门教程

静态资源 assets 与 public

本教程共 50 篇 · 第 30 篇 · 更新于 2026-08-08 · 约 7 分钟阅读

NuxtNuxt4assetspublic静态资源Vite

本节目标:分清 publicassets 两个目录各自干什么用,能在模板里正确引用资源,并避开「动态路径不生效」这个常见坑。

做网站总少不了图片、字体、图标这类静态文件。Nuxt 把它们分成两类来管,理解了这个区分,你才知道文件该放哪个文件夹、代码里又该怎么写路径。

30-1

一句话先记住:public 里的文件原样托管,assets 里的文件交给构建工具处理。

public/ 目录可以理解成一个「公共文件服务器」。放进去的文件在网站根路径 / 下直接可访问,文件名也不会被改。比如 public/img/logo.png,浏览器直接访问 /img/logo.png 就能拿到。它适合放那些不需要加工、希望 URL 稳定不变的东西:favicon、robots.txt、第三方库静态文件、已经优化好的图片。

assets/ 目录(Nuxt 4 里是 app/assets/)则相反。它里面的文件会被构建工具(默认 Vite)当作「需要加工的资源」来处理——可能是压缩、加哈希文件名做缓存、或者配合预处理器转换。处理完的产物路径通常带一串哈希,比如 logo.abc123.png,这样浏览器能安心缓存,文件更新后哈希变了又能自动失效旧缓存。

Note

assets 不是自动扫描目录,没有「放进去就生效」这回事。你得在代码里用 ~/assets/... 显式引用,构建工具才会把它打包进去。

30-2

logo.png 放进 public/img/ 后,在模板里直接写根路径即可:

<template>
  <img
    src="/img/logo.png"
    alt="网站 Logo"
  >
</template>

这种方式最直观,URL 永远稳定。Nuxt 4 的标准约定是把 public 放在 app/ 内,即 app/public/,路径写法还是 /img/logo.png,因为 public 始终映射到站点根。

Tip

放 public 的文件不会被哈希化,意味着浏览器可能长期缓存它们。所以 public 适合「几乎不会变」的文件;会频繁更新的图片,更建议走 assets 或图片优化模块(下一章)。

30-3

如果你想让图片经过压缩、哈希命名,就放进 app/assets/。在模板里用 ~/assets/ 引用:

<template>
  <img
    src="~/assets/img/hero.png"
    alt="首页主图"
  >
</template>

构建后,这个 ~/assets/img/hero.png 会被替换成带哈希的真实输出路径,并享受缓存优化。

Warning

assets 目录里的文件不会/assets/xxx.png 这种静态 URL 提供。如果你想用固定 URL 访问某个文件,请把它放进 public/。这是两个目录最容易混淆的地方。

哈希化带来的好处值得多说一句:构建工具给文件加的那串字符,是根据文件内容算出来的。内容没变,哈希就不变,浏览器可以放心地长期缓存;一旦你改了图,哈希跟着变,URL 就变了,浏览器自然会去拉新版本,不会死守着旧缓存不放。这正是 assets 适合「会更新、又追求加载速度」的资源的原因。反过来,public 文件是「按原样托管、URL 固定」,浏览器可能一直用着旧缓存,所以放 public 的更适合那些几乎不变的东西。

30-4

这是新手最高频的翻车点,务必看清。当 src 是一个写死的字符串字面量(静态路径)时,构建工具能「看见」它,于是帮你做两件事:给 public 路径加上运行时的 app.baseURL 前缀(适配部署到子路径),给 assets 路径做打包导入并哈希化。

<template>
  <!-- 静态路径会被重写:baseURL 自动生效,assets 会被哈希 -->
  <img src="/img/logo.png">
  <img src="~/assets/img/hero.png">
</template>

但如果你用 :src 在运行时拼接出路径,构建工具就「看不见」了,上面的自动处理全部不发生,字符串会原样输出:

<template>
  <!-- 这样不生效:路径是运行时拼出来的,Vite 永远拿不到它当导入 -->
  <img :src="`~/assets/img/${name}.png`">
</template>
Warning

:src 里写 ~/assets/... 是无效的,因为 ~ 别名只在构建期由 Vite 解析,运行时不存在。动态路径要么用 public(直接拼 /img/${name}.png),要么用下面讲的办法。

30-5

情况一:文件不需要处理,放 public/,用计算属性拼 URL。但要注意,这种运行时拼出来的 public 路径不会自动加 baseURL,若部署在子路径下,得自己用 useRuntimeConfig().app.baseURL 拼:

<script setup lang="ts">
const props = defineProps<{ name: string }>()

const imageUrl = computed(() => `/img/${props.name}.png`)
</script>

<template>
  <img :src="imageUrl" :alt="props.name">
</template>

情况二:文件在 assets/,但你在写代码时就已经知道所有可能的文件名,那就显式列出来用动态导入:

<script setup lang="ts">
const props = defineProps<{ theme: 'light' | 'dark' }>()

const logos = {
  light: () => import('~/assets/img/logo-light.png?url'),
  dark: () => import('~/assets/img/logo-dark.png?url'),
}

const logoUrl = (await logos[props.theme]()).default
</script>

<template>
  <img :src="logoUrl" alt="Logo">
</template>

每个导入都是字面量路径,Vite 在构建时就能找到文件,又只在运行时加载被选中的那一个。

如果同目录同扩展名的文件很多,可以用「变量动态导入」,只让文件名部分变动:

async function getImageUrl(name: string) {
  const image = await import(`~/assets/img/${name}.png?url`)
  return image.default
}

目录和扩展名必须写死,Vite 才能据此扫描「可能的文件」。再多的情况,用 import.meta.glob 一次性收集整个目录:

const images = import.meta.glob<string>('~/assets/img/*.{png,jpg,svg}', {
  query: '?url',
  import: 'default',
  eager: true,
})

function getImageUrl(name: string) {
  return images[`/assets/img/${name}.png`]
}
Note

?url 后缀告诉 Vite「我只要这个文件的 URL,不要把它当模块内容」。SSR 下记得先 await 完再使用 URL,别在未解析时就渲染。

30-6

回忆这一章的核心:public 原样托管、URL 稳定,适合 favicon、robots、不变图片;assets 交给 Vite 处理、会哈希化,适合需要压缩缓存的资源。模板里写死的 src 会被自动改写,运行时拼接的 :src 不会——动态图片要么放 public 自己拼路径,要么用 import() / import.meta.glob。下一章我们看一个专门帮图片「瘦身提速」的官方模块 @nuxt/image。

30-7 资源管理的最佳实践

合理组织静态资源能让项目更易维护。一个简单的原则:需要构建处理的放 app/assets/,不需要处理的放 public/。图片、字体、样式预处理器文件通常放 assets/;favicon、robots.txt、manifest.json 等直接需要的文件放 public/

assets/ 里引用资源时,使用 ~assets/@/assets/ 前缀。Nuxt 会通过 Vite 处理这些引用,自动加上哈希值用于缓存控制。这意味着文件内容不变时,浏览器会使用缓存;内容变化时,文件名改变,浏览器自动拉取新版本。

大文件(如视频、大型数据集)不建议打包进项目。更好的做法是放到 CDN 或对象存储服务,通过 URL 引用。这样既减小了构建产物体积,又加快了页面加载速度。

30-8 图片资源的优化策略

图片通常是网页里体积最大的资源。合理优化图片能显著提升页面加载速度。Nuxt 配合 @nuxt/image 模块(第 31 章详细讲解)提供了强大的图片处理能力,但在引入模块之前,一些基本的优化原则就值得了解。

首先,选择合适的图片格式。照片类用 WebP 或 AVIF,图标和简单图形用 SVG,需要透明背景的用 PNG。现代格式的压缩率远优于传统 JPEG 和 PNG。其次,为不同屏幕尺寸提供不同分辨率的图片。手机屏幕不需要加载桌面端的大图,通过响应式图片可以节省大量带宽。

最后,善用懒加载。不在首屏可见范围内的图片,应该延迟到用户滚动到附近时再加载。Nuxt 的 <NuxtImg> 组件默认支持懒加载,你不需要额外配置。