Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

useAsyncData и $fetch

useFetch удобен, когда странице нужен один адрес API. Если данные собираются из нескольких запросов или приходят не по HTTP, используют useAsyncData — он принимает любую асинхронную функцию. Внутри такой функции обычно вызывают $fetch — функцию Nuxt для HTTP-запросов.

$fetch

$fetch — обёртка над fetch из JavaScript. Она сама разбирает JSON, бросает ошибку при ответе с кодом 4xx или 5xx и на сервере обращается к своим /api/... напрямую, без сетевого запроса:

<!-- app/pages/index.vue -->
<script setup lang="ts">
async function showCount() {
  const posts = await $fetch('/api/posts')
  alert(`Статей в блоге: ${posts.length}`)
}
</script>
 
<template>
  <section>
    <button @click="showCount">Сколько статей?</button>
  </section>
</template>

После нажатия кнопки появится окно с числом статей. Тип ответа $fetch тоже знает: posts.length подсвечен без ошибок.

useAsyncData

useAsyncData делает с любой функцией то же, что useFetch с адресом: выполняет её на сервере и передаёт результат в браузер.

<!-- app/pages/index.vue -->
<script setup lang="ts">
const { data: posts } = await useAsyncData('posts', () => $fetch('/api/posts'))
</script>
 
<template>
  <section>
    <h2>Главная</h2>
    <p>Статей в блоге: {{ posts?.length }}</p>
  </section>
</template>

Первый аргумент — ключ, под которым Nuxt хранит данные и передаёт их в браузер. Второй — функция загрузки. На главной появится строка с числом статей. Запись useFetch('/api/posts') по сути сокращение для такого вызова.

Несколько запросов сразу

Странице статьи нужны сама статья и её комментарии. Загрузите их параллельно:

<!-- app/pages/posts/[id].vue -->
<script setup lang="ts">
const route = useRoute()
const id = route.params.id
 
const { data } = await useAsyncData(`post-${id}`, async () => {
  const [post, comments] = await Promise.all([
    $fetch(`/api/posts/${id}`),
    $fetch(`/api/posts/${id}/comments`)
  ])
  return { post, comments }
})
</script>
 
<template>
  <article v-if="data">
    <h2>{{ data.post.title }}</h2>
    <p>{{ data.post.text }}</p>
    <h3>Комментарии: {{ data.comments.length }}</h3>
    <p v-for="(comment, i) in data.comments" :key="i">
      <b>{{ comment.author }}:</b> {{ comment.text }}
    </p>
  </article>
</template>

Promise.all запускает оба запроса одновременно. По адресу /posts/1 вы увидите статью и её комментарии.

Что выбрать

ЗадачаИнструмент
Данные одного адреса API при открытии страницыuseFetch
Несколько запросов, база или своя логика при открытии страницыuseAsyncData с $fetch
Запрос по действию пользователя: кнопка, отправка формы$fetch

Частая ошибка

Вызвать $fetch прямо в <script setup>: const posts = await $fetch('/api/posts'). Страница покажется, но запрос выполнится дважды — на сервере при отрисовке и ещё раз в браузере при гидратации, ведь $fetch не передаёт результат в браузер. Для данных страницы оборачивайте $fetch в useAsyncData или берите useFetch.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.