Ресурсы (Assets)
Nuxt использует две директории для ресурсов: стилей, шрифтов и изображений.
- Содержимое
public/отдаётся с корня сервера как есть. - В
app/assets/по соглашению лежат ресурсы, которые должен обрабатывать сборщик (Vite или webpack).
Директория Public
Директория public/ используется как публичный каталог статики, доступной по фиксированному URL приложения.
Файлы из public/ доступны в коде приложения и в браузере по корневому URL /.
Пример
Изображение в public/img/ доступно по статическому URL /img/nuxt.png:
<template>
<img
src="/img/nuxt.png"
alt="Discover Nuxt"
>
</template>
Директория Assets
Nuxt использует Vite (по умолчанию) или webpack для сборки приложения. Эти инструменты в первую очередь обрабатывают JavaScript, но через плагины (Vite) или лоадеры (webpack) можно обрабатывать и другие ресурсы — стили, шрифты, SVG. На этом этапе файлы преобразуются (минификация, инвалидация кэша и т.п.).
По соглашению для таких файлов используется директория app/assets/; автоматического сканирования этой папки нет, имя можно изменить.
В коде приложения файлы из app/assets/ подключаются через путь ~/assets/.
Пример
Подключение изображения, которое будет обработано сборщиком, если для данного расширения настроена обработка:
<template>
<img
src="~/assets/img/nuxt.png"
alt="Discover Nuxt"
>
</template>
app/assets/ по статическому URL вроде /assets/my-file.png. Для статического URL используйте директорию public/.Статический и динамический 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. Если приложение развёрнуто ниже корня origin, добавьте префикс сами через 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 по умолчанию.
Если набор файлов известен заранее, перечислите 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 по умолчанию ленивый. Добавьте eager: true, если URL нужны синхронно:
const images = import.meta.glob<string>('./assets/img/*.{png,jpg,svg}', {
query: '?url',
import: 'default',
eager: true,
})
Все подходящие ресурсы всё равно попадают в сборку. Ленивые import загружают совпадения по запросу; eager glob подтягивает всё сразу и может увеличить начальный размер JavaScript или инлайнить мелкие ресурсы.
await. Паттерн Vite new URL(..., import.meta.url) не работает с SSR.