Nuxt 在最新发布的 2.12 版本中引入了新的 fetch
。Fetch 提供了一种全新的方法将数据引入 Nuxt 应用程序。
在这篇文章中,我们将探索 fetch hook 的不同功能,并尝试了解它是如何工作的。
Fetch Hook 和 Nuxt 生命周期
在 Nuxt 生命周期钩子的方面,fetch
位于 Vue 生命周期中 created
钩子之后。我们已经知道,所有 Vue 生命周期钩子都使用其 this
上下文调用。同样适用于 fetch
钩子。
Fetch 钩子在组件实例在服务器端创建后被调用。这使得 this
上下文在 fetch
内部可用。
export default {
fetch() {
console.log(this)
}
}
让我们看看这对页面组件意味着什么。
页面组件
借助 this
上下文,fetch 能够直接修改组件的数据。这意味着我们可以在不必须调度 Vuex store action 或从页面组件提交 mutation 的情况下设置组件的本地数据。
因此,Vuex 成为可选的,但并非不可能。如果需要,我们仍然可以像往常一样使用 this.$store
来访问 Vuex store。
fetch 钩子的可用性
使用 fetch
,我们可以在 **任何 Vue 组件** 中异步预取数据。这意味着,除了在 /pages
目录中找到的页面组件之外,在 /layouts
和 /components
目录中找到的每个其他 .vue
组件也可以从 fetch 钩子中受益。
让我们看看这对布局和构建块组件意味着什么。
布局组件
使用新的 fetch
,现在我们可以直接从布局组件中进行 API 调用。在 v2.12 发布之前,这是不可能的。
可能的用例
- 从后端在 Nuxt 布局中获取配置数据以动态生成页脚和导航栏
- 在导航栏中获取与用户相关的数据(例如,用户资料、购物车商品数量)
- 在
layouts/error.vue
上获取与站点相关的数据
构建块(子/嵌套)组件
由于子组件中也提供了 fetch
钩子,因此我们可以将一些数据获取任务从页面级组件中卸载,并将它们委托给嵌套组件。在 v2.12 发布之前,这也是不可能的。
这在很大程度上减少了路由级组件的责任。
**可能的用例 -** 我们仍然可以将 props 传递给子组件,但是如果子组件需要有自己的数据获取逻辑,那么现在它们可以了!
多个 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
};
我们有三个键,
- **Pending -** 允许您在客户端调用 fetch 时显示占位符
- **Error -** 允许您显示错误消息
- **Timestamp -** 显示最后一次 fetch 的时间戳,这对于使用
keep-alive
进行缓存很有用
然后,这些键直接在组件的模板区域中使用,以在从 API 获取数据的过程中显示相关的占位符。
<template>
<div>
<p v-if="$fetchState.pending">Fetching posts...</p>
<p v-else-if="$fetchState.error">Error while fetching posts</p>
<ul v-else>
…
</ul>
</div>
</template>
当在 **组件级别** 发生错误时,我们可以通过在 fetch 钩子中检查 process.server
并随后使用 throw new Error()
语句在服务器端设置 HTTP 状态代码。
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 {
// set status code on server and
if (process.server) {
this.$nuxt.context.res.statusCode = 404
}
// use throw new Error()
throw new Error('Post not found')
}
}
以这种方式设置 HTTP 状态代码 **对于正确的 SEO 很有用**。
Fetch 作为方法
新的 fetch 钩子也充当一个方法,可以在用户交互时或从组件方法以编程方式调用。
<!-- from template in template -->
<button @click="$fetch">Refresh Data</button>
// from component methods in script section
export default {
methods: {
refresh() {
this.$fetch()
}
}
}
使 Nuxt 页面性能更高
我们可以使用 :keep-alive-props
prop 和 activated
钩子,使用新的 fetch 钩子使 Nuxt 页面组件的性能更高。
Nuxt 允许 **在内存中缓存一定数量的页面** 及其获取的数据。并且还允许 **添加几秒钟** 才能重新获取数据。
为了使上述任何方法起作用,我们必须在通用 <nuxt />
和 <nuxt-child
> 组件中使用 keep-alive
prop。
<template>
<div>
<nuxt keep-alive />
</div>
</template>
此外,我们可以将 :keep-alive-props
传递给 <nuxt />
组件以缓存一定数量的页面及其获取的数据。
:keep-alive-props
prop 允许我们指示在我们在站点中其他位置导航时应保留在内存中的最大页面数。
<nuxt keep-alive :keep-alive-props="{ max: 10 }" />
以上是提高页面性能的一种更高级别和通用的方法,而下一个方法则深入到通过使用 $fetchState
的 timestamp
属性并将其与重新获取数据之前的延迟秒数进行比较来优化 fetch 请求调用。
Vue 的 activated
钩子在这里与 Nuxt 的 keep-alive
prop 一起使用以重新获取数据。
export default {
activated() {
// Call fetch again if last fetch more than a minute ago
if (this.$fetchState.timestamp <= Date.now() - 60000) {
this.$fetch()
}
}
}
asyncData vs Fetch
就页面组件而言,新的 fetch
看起来与 asyncData()
非常相似,因为它们都处理本地数据。但有一些关键区别值得注意,如下所示。
从 Nuxt 2.12 开始,asyncData
方法仍然是一个活动功能。让我们检查一下 asyncData
和新的 fetch
之间的一些关键区别。
asyncData
asyncData
仅限于页面级组件this
上下文不可用- 通过 **返回** 数据来添加有效负载
export default {
async asyncData(context) {
const data = await context.$axios.$get(
`https://jsonplaceholder.typicode.com/todos`
)
// `todos` does not have to be declared in data()
return { todos: data.Item }
// `todos` is merged with local data
}
}
新 Fetch
fetch
在所有 Vue 组件中可用this
上下文可用- 只需 **修改** 本地数据
export default {
data() {
return {
todos: []
}
},
async fetch() {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/todos`
)
// `todos` has to be declared in data()
this.todos = data
}
}
Nuxt 2.12 之前的 Fetch
如果您使用 Nuxt 一段时间了,那么您就会知道 fetch
的先前版本有很大不同。
这是一个重大更改吗?
不,不是。实际上,旧的 fetch 仍然可以通过将
context
作为第一个参数传递来使用,以避免现有 Nuxt 应用程序中的任何重大更改。
以下是与 v2.12 **之前** 和 **之后** 相比,fetch
钩子的一些显著变化列表。
1. fetch
钩子的调用顺序
**之前 -** fetch
钩子在初始化组件之前调用,因此 this
在 fetch 钩子内部不可用。
**之后 -** 当访问路由时,fetch
在组件实例在服务器端创建后被调用。
2. this
vs context
**之前 -** 我们可以在页面级组件上访问 Nuxt context
,前提是 context
作为第一个参数传递。
export default {
fetch(context) {
// …
}
}
**之后 -** 我们可以像 Vue 客户端钩子一样访问 this
上下文,无需传递任何参数。
export default {
fetch() {
console.log(this)
}
}
3. fetch
钩子的可用性
**之前 -** 只有页面(路由级)组件才能在服务器端获取数据。
**之后 -** 现在,我们可以在任何 Vue 组件中异步预取数据。
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: 'Data not found' })
来显示自定义错误页面。
结论
新的 fetch 钩子带来了许多改进,并在获取数据以及以全新的方式组织路由级和构建块组件方面提供了更大的灵活性!
当您计划和设计需要在同一路由中进行多个 API 调用的新 Nuxt 项目时,它肯定会让您有所不同的思考。
希望本文能帮助您了解新的 fetch
功能。我很想看看您用它构建了什么。