通过 100 多个技巧的集合学习 Nuxt!
发布·  

了解 Nuxt 2.12 中 fetch 的工作原理

探索 fetch 钩子的不同功能,并学习将数据引入 Nuxt 应用程序的全新方法。

Nuxt 在最新发布的 2.12 版本中引入了一个新的 fetch。Fetch 提供了一种将数据引入 Nuxt 应用程序的全新方法。

在这篇文章中,我们将探索 fetch 钩子的不同功能,并尝试了解它的工作原理。

Fetch 钩子和 Nuxt 生命周期

就 Nuxt 生命周期钩子而言,fetch 位于 Vue 生命周期中 created 钩子之后。正如我们已经知道的那样,所有 Vue 生命周期钩子都使用它们的 this 上下文调用。这同样适用于 fetch 钩子。

New fetch in Nuxt lifecycle

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
};

我们有三个键,

  1. Pending - 让您在客户端调用 fetch 时显示占位符
  2. Error - 让您显示错误消息
  3. 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。

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

此外,我们可以将 :keep-alive-props 传递给 <nuxt /> 组件,以缓存一定数量的页面以及它们获取的数据。

:keep-alive-props prop 允许我们指示在我们在站点内其他位置导航时应保留在内存中的最大页数。

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

以上是提高页面性能的一种方法,它更高级和通用,而下一种方法则深入优化 fetch 请求调用,方法是使用 $fetchStatetimestamp 属性,并将其与重新获取数据之前的秒延迟数进行比较。

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 与 Fetch

就页面组件而言,新的 fetch 看起来与 asyncData() 太相似了,因为它们都处理本地数据。但是,以下是一些值得注意的关键差异。

截至 Nuxt 2.12,asyncData 方法仍然是一个活跃的功能。让我们检查一下 asyncData 和新的 fetch 之间的一些关键区别。

asyncData

  1. asyncData 仅限于页面级组件
  2. this 上下文不可用
  3. 通过返回数据添加 payload
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

  1. fetch 在所有 Vue 组件中都可用
  2. this 上下文可用
  3. 简单地改变本地数据
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. thiscontext

之前 - 鉴于 context 作为第一个参数传递,我们可以在页面级组件上访问 Nuxt 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 功能。我很想看看您用它构建了什么。