资源
Nuxt 为你的资源提供了两种处理方式。
Nuxt 使用两个目录来处理样式表、字体或图片等资源。
public/目录的内容会按原样在服务器根路径下提供。app/assets/目录按约定存放所有你希望构建工具(Vite 或 webpack)处理的资源。
公共目录(Public Directory)
public/ 目录用作静态资源的公共服务器,可通过应用定义的 URL 公开访问。
你可以从应用代码中或通过根 URL / 从浏览器中获取 public/ 目录中的文件。
示例(Example)
例如,引用 public/img/ 目录中的图片文件,可通过静态 URL /img/nuxt.png 访问:
<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)
例如,引用一个将被构建工具处理的图片文件(前提是已配置处理该扩展名):
<template>
<img
src="~/assets/img/nuxt.png"
alt="Discover Nuxt"
>
</template>
静态 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 引用:
<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:
<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 体积或内联小型资源。
new URL(..., import.meta.url) 模式 在 SSR 下不可用。