跳到主要内容

资源

Nuxt 为你的资源提供了两种处理方式。

Nuxt 使用两个目录来处理样式表、字体或图片等资源。

  • public/ 目录的内容会按原样在服务器根路径下提供。
  • app/assets/ 目录按约定存放所有你希望构建工具(Vite 或 webpack)处理的资源。

公共目录(Public Directory)

public/ 目录用作静态资源的公共服务器,可通过应用定义的 URL 公开访问。

你可以从应用代码中或通过根 URL / 从浏览器中获取 public/ 目录中的文件。

示例(Example)

例如,引用 public/img/ 目录中的图片文件,可通过静态 URL /img/nuxt.png 访问:

app/app.vue
<template>
  <img
    src="/img/nuxt.png"
    alt="Discover Nuxt"
  >
</template>

资源目录(Assets Directory)

Nuxt 使用 Vite(默认)或 webpack 来构建和打包你的应用。这些构建工具的主要功能是处理 JavaScript 文件,但可以通过 插件(Vite)或 loader(webpack)进行扩展,以处理样式表、字体或 SVG 等其他类型的资源。这一步会转换原始文件,主要用于性能或缓存目的(例如样式表压缩或浏览器缓存失效)。

按约定,Nuxt 使用 app/assets/ 目录来存放这些文件,但该目录没有自动扫描功能,你也可以使用任何其他名称。

在应用代码中,你可以通过 ~/assets/ 路径引用 app/assets/ 目录中的文件。

示例(Example)

例如,引用一个将被构建工具处理的图片文件(前提是已配置处理该扩展名):

app/app.vue
<template>
  <img
    src="~/assets/img/nuxt.png"
    alt="Discover Nuxt"
  >
</template>
Nuxt 不会在 /assets/my-file.png 这样的静态 URL 下提供 app/assets/ 目录中的文件。如果你需要一个静态 URL,请使用 public/ 目录。

静态 src 与动态 src(Static vs. Dynamic src

src 是模板中的静态字符串字面量时,构建工具会将其重写为一个在运行时解析最终 URL 的辅助函数。像 /img/nuxt.png 这样的公共路径会被包裹,以便在页面渲染时应用你的 app.baseURL,而像 ~/assets/img/nuxt.png 这样的打包路径还会变成一个 import,解析到带哈希值的输出文件。

<template>
  <!-- Static paths are rewritten: app.baseURL is applied at runtime, and the bundled file is hashed. -->
  <img src="/img/nuxt.png">
  <img src="~/assets/img/nuxt.png">
</template>

由于 app.baseURL 在运行时应用,静态公共路径即使在 base URL 仅在部署时才知道的情况下也能工作(例如通过 NUXT_APP_BASE_URL 设置),并且无论文件是否经过构建处理都生效。这种解析只发生在构建工具能看到的字面量路径上。

在运行时拼接得到的绑定 :src 对构建工具来说是不透明的,因此不会发生上述任何重写。字符串会按原样使用:

<template>
  <!-- This does not work: the path is built at runtime, so Vite never sees it as an import. -->
  <img :src="`~/assets/img/${name}.png`">
</template>

因此,运行时拼接的公共路径(如 /img/${name}.png不会被加上 app.baseURL 前缀。如果你的应用部署在源站根路径之下,请自行使用 useRuntimeConfig().app.baseURL(例如通过 joinURL)添加前缀。

以下章节介绍当路径仅在运行时才知道时,如何处理上述每种情况。

公共资源(Public Assets)

如果文件不需要被处理或哈希,请将它们放入 public/ 目录,并通过 URL 引用:

app/app.vue
<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>

public/ 中的文件会保留其原始文件名。

使用 Vite 打包资源(Bundled Assets with Vite)

以下方法特定于 Vite,即 Nuxt 默认的构建工具。

当可能的文件已知时,显式列出它们的 import:

app/app.vue
<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="Nuxt"
  >
</template>

每个 import 都有字面量路径,因此 Vite 可以在构建时找到两个文件,同时只在运行时加载被选中的模块。

当许多文件共享同一目录和扩展名时,使用动态变量 import 来代替逐个列出文件:

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

在此示例中,只有文件名可以是动态的。将目录和扩展名保留在 import 中,Vite 就能在构建时找到可能的文件。

对于更通用的模式或明确的可用文件映射,请使用 import.meta.glob

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

async function getImageUrl (name: string) {
  const load = images[`./assets/img/${name}.png`]

  if (!load) {
    throw new Error(`Unknown image: ${name}`)
  }

  return await load()
}

Glob import 默认是惰性的(lazy)。如果 URL 必须同步可用,请添加 eager: true

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

每个匹配的资源仍会包含在构建输出中。惰性 import 按需加载每个匹配项,而 eager glob 会立即加载所有匹配项,这可能会增加初始 JavaScript 体积或内联小型资源。

在使用服务端渲染的标记中使用其 URL 之前,请先 await 惰性 import。Vite 的 new URL(..., import.meta.url) 模式 在 SSR 下不可用。