数据获取
Nuxt 中有两种从 API 获取数据的方式,可以使用 fetch 方法或 asyncData 方法。
Nuxt 支持 Vue 在客户端应用中的传统数据获取模式,例如在组件的 mounted() 钩子中获取数据。但在通用应用中,需要使用 Nuxt 特有的钩子在服务端渲染期间渲染数据,从而确保页面渲染时所有必要数据都已存在。
Nuxt 提供了两个用于异步加载数据的钩子:
-
asyncData。该钩子只能在 页面 组件中使用。与fetch不同,它在客户端渲染期间不显示加载占位符,而是阻塞路由导航直到钩子解析完成,失败时显示错误页面。 -
fetch(Nuxt 2.12 及以上)。可以放置在任何组件中,提供渲染(客户端渲染期间的)加载状态和错误的快捷方式。
这些钩子可以与你选择的 任何数据获取库 配合使用。推荐使用 @nuxt/http 或 @nuxt/axios 向 HTTP API 发送请求。更多关于这些库的信息(例如设置认证请求头)可以在各自的文档中找到。
fetch 或 asyncData,同时在组件或页面中也定义了,mixin 函数将被覆盖而不会被调用。fetch 钩子
fetch 钩子。如果 fetch() 接收 context 参数,则会被视为旧版 fetch 钩子。该功能已废弃,请用 asyncData 或匿名中间件 (anonymous middleware) 替代。fetch 是一个在服务端渲染时于组件实例创建后调用、在客户端于导航时调用的钩子。fetch 钩子应返回一个 promise(显式地,或通过 async/await 隐式地),该 promise 在以下时机解析:
- 在服务器上,初始页面渲染之前
- 在客户端上,组件挂载之后
使用方法
数据获取 (Fetching)
在 fetch 钩子内,可以通过 this 访问组件实例。
data() 中声明。这样才能将获取到的数据赋值给这些属性。修改 fetch 行为
fetchOnServer: Boolean 或 Function(默认值:true)。服务器渲染页面时调用 fetch()。
fetchKey: String 或 Function(默认为组件的作用域 ID 或组件名称),用于标识组件 fetch 结果的键(或生成唯一键的函数)。(Nuxt 2.15 及以上有效,详细信息见 GitHub 拉取请求 )。在对服务器渲染的页面进行水合时,该键用于将服务端 fetch() 的结果映射到客户端组件数据。详情请参阅 PR。
fetchDelay: Integer(默认值:200)。设置最小执行时间(毫秒)(用于防止快速闪烁)。
当 fetchOnServer 为 falsy(false 或求值为 false 的值)时,fetch 仅在客户端调用,服务器渲染组件期间 $fetchState.pending 返回 true。
export default {
data: () => ({
posts: []
}),
async fetch() {
this.posts = await this.$http.$get('https://api.nuxtjs.dev/posts')
},
fetchOnServer: false,
// 多个组件可以返回相同的 `fetchKey`,Nuxt 会分别追踪它们
fetchKey: 'site-sidebar',
// 作为替代方案,如果需要更多控制,可以传入一个能访问组件实例的函数
// 该函数在 `created` 中调用,不应依赖已获取的数据
fetchKey(getCounter) {
// getCounter 是一个可调用的方法,用于在生成唯一 fetchKey 时
// 获取序列中的下一个数字
return this.someOtherData + getCounter('sidebar')
}
}
访问 fetch 状态
fetch 钩子在组件级别暴露 this.$fetchState,包含以下属性:
-
pending是一个Boolean,表示在 客户端 调用fetch时是否显示占位符 -
error是null或 fetch 钩子抛出的Error -
timestamp是最后一次 fetch 的时间戳,可用于通过keep-alive缓存
除了 Nuxt 调用的 fetch,还可以在组件内通过 this.$fetch() 手动调用 fetch(例如重新加载异步数据)。
<template>
<p v-if="$fetchState.pending">Fetching mountains...</p>
<p v-else-if="$fetchState.error">An error occurred :(</p>
<div v-else>
<h1>Nuxt Mountains</h1>
<ul>
<li v-for="mountain of mountains">{{ mountain.title }}</li>
</ul>
<button @click="$fetch">Refresh</button>
</div>
</template>
<script>
export default {
data() {
return {
mountains: []
}
},
async fetch() {
this.mountains = await fetch(
'https://api.nuxtjs.dev/mountains'
).then(res => res.json())
}
}
</script>
this.$nuxt.context 访问 Nuxt context 。监听查询字符串变化
默认情况下,查询字符串变化不会触发 fetch 钩子。要监听查询字符串变化,可以在侦听器中添加 $route.query 并调用 $fetch:
export default {
watch: {
'$route.query': '$fetch'
},
async fetch() {
// 查询字符串变化时也会被调用
}
}
缓存
可以在 <nuxt/> 或 <nuxt-child/> 组件中使用 keep-alive 指令来保存已访问页面的 fetch 调用:
<template>
<nuxt keep-alive />
</template>
还可以通过向 <nuxt> 组件传递 keep-alive-props 属性来指定传给 <keep-alive> 的 props 。
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />
在内存中保存 10 个页面组件。
错误处理
fetch() 内使用 Nuxt 的 redirect 或 error 方法。应改为在组件内使用 $fetchState.error 处理错误。获取数据发生错误时,通过 $fetchState.error 检查并显示错误信息。
<template>
<div>
<p v-if="$fetchState.pending">Loading....</p>
<p v-else-if="$fetchState.error">Error while fetching mountains</p>
<ul v-else>
<li v-for="(mountain, index) in mountains" :key="index">
{{ mountain.title }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
mountains: []
}
},
async fetch() {
this.mountains = await fetch(
'https://api.nuxtjs.dev/mountains'
).then(res => res.json())
}
}
</script>
使用 activated 钩子
Nuxt 将最后一次调用 fetch 的时间(包括 SSR)直接存储在 this.$fetchState.timestamp 中。将该属性与 activated 钩子结合使用,可以为 fetch 添加 30 秒缓存:
<template> ... </template>
<script>
export default {
data() {
return {
posts: []
}
},
activated() {
// 如果距上次 fetch 超过 30 秒,则重新调用 fetch
if (this.$fetchState.timestamp <= Date.now() - 30000) {
this.$fetch()
}
},
async fetch() {
this.posts = await fetch('https://api.nuxtjs.dev/posts').then(res =>
res.json()
)
}
}
</script>
如果最后一次 fetch 调用在 30 秒以内,则导航到同一页面时不会再次调用 fetch。
Async Data
asyncData 是另一个用于通用数据获取的钩子。与需要设置组件实例属性(或分发 Vuex action)来保存异步状态的 fetch 不同,asyncData 只需将返回值合并到组件数据中。以下是使用 @nuxt/http 库的示例:
<template>
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.description }}</p>
</div>
</template>
<script>
export default {
async asyncData({ params, $http }) {
const post = await $http.$get(`https://api.nuxtjs.dev/posts/${params.id}`)
return { post }
}
}
</script>
与 fetch 不同,asyncData 钩子返回的 promise 在 路由切换期间 解析,这意味着客户端切换时不会显示"加载占位符"(但可以使用加载进度条 向用户展示加载状态)。Nuxt 会等待 asyncData 钩子完成后再导航到下一个页面或显示错误页面 。
该钩子只能用于页面级组件。与 fetch 不同,asyncData 无法访问组件实例(this),而是接收 context 作为参数。可以使用 asyncData 获取数据,Nuxt 会自动将返回的对象与组件数据进行浅合并(shallow merge)。
后续示例将使用推荐的 @nuxt/http 从 API 获取数据。
组件的异步数据?
由于组件没有 asyncData 方法,无法在组件内直接从服务器获取异步数据。要绕过此限制,有三种基本选项:
-
使用 Nuxt 2.12 及以上版本启用的新
fetch钩子 -
在
mounted钩子中调用 API 并在加载时设置数据属性。缺点:服务端渲染中不起作用。 -
在页面组件的
asyncData方法中调用 API,并将数据作为属性传递给子组件。服务器渲染正常工作。缺点:页面的asyncData需要加载其他组件的数据,可能导致可读性下降。
监听查询字符串变化
默认情况下,查询字符串变化不会触发 asyncData 方法。如果想改变此行为(例如构建分页组件),可以在页面组件的 watchQuery 属性中设置要监听的参数。