Nuxt Fetch. How?
Начиная с версии 2.12 в Nuxt появился новый подход к получению данных приложением.
Хук Fetch и жизненный цикл Nuxt
Сверху вниз
NuxtServerInit
|
Route Middleware
|
validate
|
asyncData()
|
beforeCreate()
|
created()
|
[$fetchState.pending = true]
>>> fetch() <<<
|
mounted()
[$fetchState.pending = false]
Хук fetch()
вызывается после того, как экземпляр компонента создан на стороне сервера. Это делает доступным контекст this
внутри fetch()
.
export default {
fetch() {
console.log(this)
}
}
Page Components
И так что же это означает для компонентов страниц (pages
). При помощи контекста this
, fetch может изменить данные компонента напрямую. Это значит, что можно определить локальные данные компонента, без необходимости вызывать события/фиксировать изменения через Vuex Store для Page компонента.
В результате Vuex становиться не обязательным. Конечно по прежнему можно использовать this.$store
, как обычно для доступа к Vuex Store, если необходимо.
Доступность хука fetch()
Используя fetch()
можно асинхронно загружать данные в любых компонентах. Это означает, что не только компоненты в директории /pages/*
, но и любые другие .vue
компоненты в /layouts
и /components
могут использовать этот подход.
Layouts (макеты)
Теперь, используя новый подход с fetch()
, возможно делать запросы непосредственно из Layout
компонентов. Что было невозможно до версии Nuxt 2.12.
Возможное применение:
- Загрузка конфигурации с сервера, для динамической генерации футер (footer) или навигации (navbar)
- Загрузка данных пользователя, например профиль, корзина в navbar
- Загрузка соответствующих сайту данных в
layouts/error.vue
Блочные (Child/Nested) компоненты
Используя fetch()
в дочерних, можно исключить загрузку некоторых данных из уровня компонента страницы Page
, делегировать часть задач загрузки в дочерние компоненты. Все еще можно передать параметры в дочерние компоненты, но данные каждый компонент сможет загрузить самостоятельно.
Порядок выполнения при множественном использовании fetch()
Очевидный вопрос, если каждый компонент может сам загружать данные, то в каком порядке происходит загрузка.
fetch()
выполняется на стороне сервера один раз (при первом запросе Nuxt приложения) и затем выполняется на стороне клиента при запросе соответствующего маршрута. Но поскольку можно определить fetch()
для каждого компонента, все они будут исполняться последовательно в соответствие с иерархией.
Отключение fetch()
на стороне сервера
Можно отключить выполнение fecth()
на стороне сервера, выполнение будет проходить только на стороне клиента:
export default {
fetchOnServer: false
}
Свойство $fetchState.pending
на стороне сервера будет установлено в true
Обработка ошибок
Обработка ошибок теперь происходит на уровне компонента. Поскольку данные загружаются асинхронно, fetch()
предлагает объект $fetchState
для контроля за состоянием запроса:
$fetchState = {
pending: true | false,
error: null | [],
timestamp: Integer
}
pending
- поможет в отображении статуса загрузкиError
- сообщения об ошибкахTimestamp
- отображает временную метку последнего запроса, которую можно использовать при кешировании совместно сkeep-alive
<template>
<div>
<p v-if="$fetchState.pending">Loading data...</p>
<p v-else-if="$fetchState.error">Error while lodaing!</p>
<ul v-else>
<li v-for="..." />
</ul>
</div>
</template>
При возникновении ошибки на уровне компонента, можно установить HTTP статус на стороне сервера используя проверку process.server
внутри fetch()
и передать через throw new Error()
async fetch() {
const post = await fetch('//url/post').then((res) => res.json())
if (post.id === this.$route.params.id) {
this.post = post
} else {
// on server
if (process.server) {
this.$nuxt.context.res.statusCode = 404
}
// throw
throw new Error('Post not found')
}
}
Установка правильного статуса ответа кране важна для SEO.
fetch()
как метод
fetch()
можно использовать как обычный метод. Например для программного обновления данных
<button @click="$fetch">refresh</button>
или в методе компонента
export default {
methods: {
refresh() {
this.$fetch()
}
}
}
Эффективность страниц
Используем :keep-alive-props
, хук activated
и хук fetch()
вместе, для повышения эффективности страниц.
Nuxt может кешировать в памяти страницы вместе с извлеченными данными. И указать количество секунд, через которое следует запросить данные снова.
Используем keep-alive
на <nuxt />
и <nuxt-child />
<template>
<div>
<nuxt keep-alive />
</div>
</template>
для добавления параметров используем :keep-alive-props
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />
Это один из способов повышения производительности страниц. Это общий универсальный способ. Другой подход использовать свойство $fetchData.timestamp
для более точной настройки. Используется Vue хук activated
export default {
activated() {
// more than 1 minute ago
if (this.$fetchState.timestamp <= Date.now() - 60000) {
this.$fetch()
}
}
}
asyncData vs Fetch
asyncData
asyncData
работает только для page-компонентовthis
контекст не доступен- Добавляет результат возвращая значение
export default {
async asyncData(context) {
const data await context.$axios.$get('//fetch/url')
return {
todos: data.item
}
}
}
новый fetch()
- Доступен во всех Vue компонентах
this
контекст доступен- Просто изменяет локальные данные
export default {
data() {
return {
todos: []
}
},
async fetch() {
const { data } = await axios.get('//fetch/url')
this.todos = data
}
}
И так
Новый fetch()
предлагает множество улучшений, большую гибкость при получении данных и организации маршрутов, а также возможность загружать данные на уровне отдельных строительных блоков страницы (pages, layouts, componnets). Новый подход предполагает множество запросов к API для одной страницы, что требует внимательней относиться к проектированию страниц и приложения в целом.