Nuxt Nation conference is coming. Join us on November 12-13.
Релиз·  

Понимание работы fetch в Nuxt 2.12

Изучение различных функций хука fetch и изучение нового способа внесения данных в приложения Nuxt.

Nuxt представляет новый fetch с последним релизом версии 2.12. Fetch предоставляет совершенно новый способ получениях данных в приложениях Nuxt.

В этом посте мы рассмотрим различные функции хука fetch и попытаемся понять, как он работает.

Хук Fetch и жизненный цикл Nuxt

С точки зрения хуков жизненного цикла Nuxt, fetch находится в рамках жизненного цикла Vue после хука created. Как мы уже знаем, все хуки жизненного цикла Vue вызываются с их контекстом this. То же самое относится и к хуку fetch.

Новый fetch в жизненном цикле Nuxt

Хук fetch вызывается после создания экземпляра компонента на сервере. Это делает контекст this доступным внутри fetch.

export default {
  fetch() {
    console.log(this)
  }
}

Давайте посмотрим, что это может означать для компонентов страниц.

Компоненты страниц

С помощью контекста this fetch может напрямую изменять данные компонента. Это означает, что мы можем устанавливать локальные данные компонента, не прибегая к dispatch-действия Vuex или commit-мутациям из компонента страницы.

В результате Vuex становится необязательным, но не невозможным. Мы все еще можем использовать this.$store как обычно для доступа к хранилищу Vuex, если это необходимо.

Доступность хука fetch

С fetch мы можем предварительно загружать данные асинхронно в любом Vue-компоненте. Это означает, что помимо компонентов страниц, найденных в директории /pages, каждый другой .vue компонент, найденный в директориях /layouts и /components, также может извлечь выгоду из хука fetch.

Давайте посмотрим, что это может означать для лаяутов и компонентов строительных блоков.

Компоненты лаяута

Используя новый fetch, теперь мы можем делать API-вызовы непосредственно из компонентов лаяута. Это было невозможно до выхода версии 2.12.

Возможные варианты использования

  • Получение данных конфигурации с сервера в лаяутах Nuxt для динамического создания футера и навигационной панели
  • Получение данных, связанных с пользователем (например, профиль пользователя, количество товаров в корзине) в навигационной панели
  • Получение данных, относящихся к сайту, на layouts/error.vue

Строительные блоки (Дочерние/Вложенные компоненты)

С доступным хуком fetch в дочерних компонентах мы можем переложить часть задач по получению данных с компонентов уровня страницы на вложенные компоненты. Это также было невозможно до выхода версии 2.12.

Это значительно уменьшает ответственность компонентов уровня маршрута.

Возможный вариант использования - Мы все еще можем передавать пропсы дочерним компонентам, но если дочерним компонентам нужна собственная логика получения данных, теперь они могут делать это сами!

Порядок вызова нескольких хуков fetch

Поскольку каждый компонент может иметь свою собственную логику получения данных, вы можете спросить, в каком порядке будут вызываться каждый из них?

Хук fetch вызывается на сервере один раз (при первом запросе к приложению Nuxt) и затем на клиенте при навигации по дальнейшим маршрутам. Но поскольку мы можем определить один хук fetch для каждого компонента, хуки fetch вызываются в последовательности их иерархии.

Отключение fetch на сервере

Кроме того, мы можем даже отключить fetch на сервере, если это необходимо.

export default {
  fetchOnServer: false
}

И таким образом, хук fetch будет вызываться только на клиенте. Когда fetchOnServer установлен в false, $fetchState.pending становится true, когда компонент отображается на сервере.

Обработка ошибок

Новый fetch обрабатывает ошибки на уровне компонента. Давайте посмотрим как.

Поскольку мы получаем данные асинхронно, новый fetch() предоставляет объект $fetchState для проверки, завершился ли запрос, и прошел ли он успешно.

Ниже представлен объект $fetchState.

$fetchState = {
  pending: true | false,
  error: null | {},
  timestamp: Integer
};

У нас есть три ключа:

  1. Pending - позволяет отображать плейсхолдер, когда fetch вызывается на клиенте
  2. Error - позволяет отображать сообщение об ошибке
  3. Timestamp - показывает временную метку последнего fetch, что полезно для кэширования с keep-alive

Эти ключи затем используются непосредственно в области шаблона компонента для отображения соответствующих плейсхолдеров в процессе получения данных из API.

<template>
  <div>
    <p v-if="$fetchState.pending">Получение постов...</p>
    <p v-else-if="$fetchState.error">Ошибка при получении постов</p>
    <ul v-else>
    </ul>
  </div>
</template>

Когда возникает ошибка на уровне компонента, мы можем установить HTTP-код состояния на сервере, проверив process.server в хуке fetch, и затем выполнить оператор throw new Error().

async fetch() {
    const post = await fetch(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
        .then((res) => res.json())

    if (post.id === this.$route.params.id) {
        this.post = post
    } else {
        // установить код состояния на сервере и
        if (process.server) {
            this.$nuxt.context.res.statusCode = 404
        }
        // использовать throw new Error()
        throw new Error('Пост не найден')
    }
}

Установка HTTP-кода состояния таким образом полезна для правильного SEO.

Fetch как метод

Новый хук fetch также действует как метод, который может быть вызван при взаимодействии пользователя или вызван программно из методов компонента.

<!-- из шаблона в шаблоне  -->
<button @click="$fetch">Обновить данные</button>
// из методов компонента в разделе скрипта
export default {
  methods: {
    refresh() {
      this.$fetch()
    }
  }
}

Повышение производительности страниц Nuxt

Мы можем использовать пропс :keep-alive-props и хук activated для повышения производительности компонентов страниц Nuxt с помощью нового хука fetch.

Nuxt позволяет кэшировать определенное количество страниц в памяти вместе с их полученными данными. А также позволяет добавить количество секунд до того, как мы сможем повторно получить данные.

Для работы любого из вышеуказанных методов мы должны использовать пропсkeep-alive в общих компонентах <nuxt /> и <nuxt-child>.

layouts/default.vue
<template>
  <div>
    <nuxt keep-alive />
  </div>
</template>

Кроме того мы можем передать :keep-alive-props в компонент <nuxt />, чтобы кэшировать определенное количество страниц вместе с их полученными данными.

Пропс :keep-alive-props позволяет нам указать максимальное количество страниц, которые должны храниться в памяти, пока мы перемещаемся по сайту.

layouts/default.vue
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />

Выше представлен один из способов повышения производительности страниц, который является более высокоуровневым и общим, в то время как следующий способ углубляется в оптимизацию вызовов запросов fetch, используя свойство timestamp объекта $fetchState и сравнивая его с количеством секунд задержки до повторного получения данных.

Хук activated Vue используется здесь с пропсом keep-alive Nuxt для повторного получения данных.

export default {
  activated() {
    // Вызвать fetch снова, если последний fetch был более минуты назад
    if (this.$fetchState.timestamp <= Date.now() - 60000) {
      this.$fetch()
    }
  }
}

asyncData против Fetch

Что касается компонентов страниц, новый fetch кажется слишком похожим на asyncData(), потому что они оба имеют дело с локальными данными. Но есть некоторые ключевые различия, которые стоит отметить, как показано ниже.

Начиная с Nuxt 2.12, метод asyncData по-прежнему является активным функционалом. Давайте рассмотрим некоторые ключевые различия между asyncData и новым fetch.

asyncData

  1. asyncData ограничен только компонентами уровня страницы
  2. Контекст this недоступен
  3. Добавляет полезную нагрузку, возвращая данные
export default {
  async asyncData(context) {
    const data = await context.$axios.$get(
      `https://jsonplaceholder.typicode.com/todos`
    )
    // `todos` не нужно объявлять в data()
    return { todos: data.Item }
    // `todos` объединяется с локальными данными
  }
}

Новый Fetch

  1. fetch доступен во всех Vue-компонентах
  2. Контекст this доступен
  3. Просто изменяет локальные данные
export default {
  data() {
    return {
      todos: []
    }
  },
  async fetch() {
    const { data } = await axios.get(
      `https://jsonplaceholder.typicode.com/todos`
    )
    // `todos` должен быть объявлен в data()
    this.todos = data
  }
}

Fetch до Nuxt 2.12

Если вы работали с Nuxt какое-то время, то знаете, что предыдущая версия fetch значительно отличалась.

Это критическое изменение? Нет, это не так. На самом деле, старый fetch все еще можно использовать, передав context в качестве первого аргумента, чтобы избежать каких-либо критических изменений в ваших существующих приложениях Nuxt.

Вот список заметных изменений в хуке fetch по сравнению с до и после версии 2.12.

1. Порядок вызова хука fetch

До - Хук fetch вызывался до инициализации компонента, поэтому this не был доступен внутри хука fetch.

После - fetch вызывается после создания экземпляра компонента на сервере при доступе к маршруту.

2. this против context

До - У нас был доступ к контексту Nuxt на компонентах уровня страницы, учитывая, что context передается в качестве первого параметра.

export default {
  fetch(context) {
    // …
  }
}

После - Мы можем получить доступ к контексту this как и в хуках Vue на клиенте без передачи каких-либо параметров.

export default {
  fetch() {
    console.log(this)
  }
}

3. Доступность хука fetch

До - Только компоненты страниц (уровня маршрута) могли получать данные на сервере.

После - Теперь мы можем предварительно загружать данные асинхронно в любом Vue-компоненте.

4. 4. Порядок вызова хука fetch

До - fetch мог быть вызван на сервере один раз (при первом запросе к приложению Nuxt) и на клиенте при навигации по дальнейшим маршрутам.

После - Новый fetch такой же, как старый fetch, но…

…поскольку мы можем иметь один fetch для каждого компонента, хуки fetch вызываются в последовательности их иерархии.

5. Обработка ошибок

До - Мы использовали функцию context.error, которая показывала пользовательскую страницу ошибки при возникновении ошибки во время API-вызовов.

После - Новый fetch использует объект $fetchState для обработки ошибок в области шаблона во время API-вызовов.

Обработка ошибок выполняется на уровне компонента.

Значит ли это, что мы больше не можем показывать пользователям пользовательскую страницу ошибки, как мы делали до Nuxt 2.12?

Да, мы можем, но только с asyncData(), когда речь идет о данных компонентов уровня страницы. При использовании fetch мы можем использовать this.$nuxt.error({ statusCode: 404, message: 'Данные не найдены' }), чтобы показать пользовательскую страницу ошибки.

Заключение

Новый хук fetch приносит множество улучшений и обеспечивает большую гибкость в получении данных и организации компонентов уровня маршрута и строительных блоков новым способом!

Это, безусловно, заставит вас думать немного иначе, когда вы планируете и проектируете новое приложение Nuxt, требующее нескольких API-вызовов в пределах одного маршрута.

Надеюсь, эта статья помогла вам ознакомиться с новой функцией fetch. Буду рад увидеть, что вы создадите с её помощью.

← Вернуться к блогу