资源管理
Nuxt 使用两个目录来处理诸如样式表、字体或图像之类的资源。
public/目录中的内容将按原样在服务器根目录提供服务。app/assets/目录按约定包含你希望构建工具(Vite 或 webpack)处理的所有资源。
Public 目录
public/ 目录作为公共服务器,用于静态资源,这些资源可通过应用的定义 URL 公共访问。
你可以通过根 URL / 从你的应用代码或浏览器中访问 public/ 目录中的文件。
示例
例如,引用 public/img/ 目录中的图像文件,该文件可通过静态 URL /img/nuxt.png 访问:
<template>
<img
src="/img/nuxt.png"
alt="发现 Nuxt"
>
</template>
Assets 目录
Nuxt 使用 Vite(默认)或 webpack 构建和打包你的应用。这些构建工具的主要功能是处理 JavaScript 文件,但它们可以通过 插件(针对 Vite)或 加载器(针对 webpack)扩展来处理其它类型的资源,比如样式表、字体或 SVG。这一步会转换原始文件,主要是为了性能或缓存目的(例如样式表压缩或浏览器缓存失效)。
按约定,Nuxt 使用 app/assets/ 目录来存放这些文件,但此目录没有自动扫描功能,你也可以使用其它名称。
在你的应用代码中,可以使用 ~/assets/ 路径来引用位于 app/assets/ 目录中的文件。
示例
例如,引用一个图像文件,如果构建工具配置了处理该文件扩展名,则此文件将被处理:
<template>
<img
src="~/assets/img/nuxt.png"
alt="发现 Nuxt"
>
</template>
静态 vs. 动态 src
当模板中的 src 是静态字符串字面量时,构建工具会将其重写为一个运行时辅助函数,以解析最终 URL。像 /img/nuxt.png 这样的公共路径会被包装起来,以便在页面渲染时应用你的 app.baseURL,而像 ~/assets/img/nuxt.png 这样的打包路径还会进一步变成一个导入,解析为带哈希的输出文件。
<template>
<!-- 静态路径会被重写:运行时会应用 app.baseURL,并且打包文件会带有哈希。 -->
<img src="/img/nuxt.png">
<img src="~/assets/img/nuxt.png">
</template>
由于 app.baseURL 是在运行时应用的,即使基础 URL 只在部署时才知道(例如通过 NUXT_APP_BASE_URL 设置),静态公共路径也能正常工作,而且无论该文件是否经过构建处理都可以使用。只有构建工具能够看到的字面量路径才会发生这种解析。
绑定的 :src 如果其值是在运行时组装出来的,对构建工具来说是不可见的,因此不会发生任何重写。该字符串会原样使用:
<template>
<!-- 这样不起作用:路径是在运行时构建的,因此 Vite 从未将其视为导入。 -->
<img :src="`~/assets/img/${name}.png`">
</template>
因此,像 /img/${name}.png 这样的运行时构建的公共路径不会自动添加 app.baseURL 前缀。如果你的应用部署在源站根路径下方,请自己使用 useRuntimeConfig().app.baseURL 为其添加前缀(例如通过 joinURL)。
下面的各节介绍了当路径只在运行时才知道时,如何处理每种情况。
公共资源
如果文件不需要被处理或哈希化,请将它们放在 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 的打包资源
下面的方法仅适用于 Vite,也就是 Nuxt 的默认构建器。
当可能的文件是已知的时,请显式列出它们的导入:
<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>
每个导入都有一个字面量路径,因此 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',
})
async function getImageUrl (name: string) {
const load = images[`./assets/img/${name}.png`]
if (!load) {
throw new Error(`未知图片:${name}`)
}
return await load()
}
glob 导入默认是懒加载的。如果 URL 必须同步可用,请添加 eager: true:
const images = import.meta.glob<string>('./assets/img/*.{png,jpg,svg}', {
query: '?url',
import: 'default',
eager: true,
})
每个匹配的资源仍然会包含在构建输出中。懒加载导入会按需加载每个匹配项,而 eager glob 会一次性加载所有匹配项,可能会增加初始 JavaScript 体积,或者将小资源内联。
new URL(..., import.meta.url) 模式 不适用于 SSR。