リリース·  

Nuxt 2.12におけるfetchの仕組みの理解

fetchフックのさまざまな機能を探索し、Nuxtアプリケーションにデータを取得する新しい方法を学びましょう。
Krutie Patel

クルーティー・パテル

@KrutiePatel

Nuxtは、バージョン2.12の最新リリースで新しいfetchを導入しました。Fetchは、Nuxtアプリケーションにデータを取得する新しい方法を提供します。

この投稿では、fetchフックのさまざまな機能を探索し、その仕組みを理解しようとします。

FetchフックとNuxtライフサイクル

Nuxtのライフサイクルフックの観点から見ると、fetchはVueのライフサイクルにおいてcreatedフックの後に位置します。ご存知のように、すべてのVueライフサイクルフックはthisコンテキストと共に呼び出されます。fetchフックについても同様です。

Fetchフックは、コンポーネントインスタンスがサーバー側で作成された後に呼び出されます。これにより、thisコンテキストがfetch内で利用可能になります。

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

これがページコンポーネントにとって何を意味するかを見てみましょう。

ページコンポーネント

thisコンテキストの助けを借りて、fetchはコンポーネントのデータを直接変更できます。つまり、Vuexストアアクションをディスパッチしたり、ページコンポーネントからミューテーションをコミットしたりすることなく、コンポーネントのローカルデータを設定できるということです。

その結果、Vuexはオプションになりますが、不可能ではありません。必要に応じて、通常通りthis.$storeを使用してVuexストアにアクセスできます。

fetchフックの可用性

fetchを使用すると、任意のVueコンポーネントで非同期にデータをプリフェッチできます。つまり、/pagesディレクトリにあるページコンポーネント以外にも、/layoutsおよび/componentsディレクトリにある他のすべての.vueコンポーネントもfetchフックの恩恵を受けることができます。

これがレイアウトコンポーネントとビルディングブロックコンポーネントにとって何を意味するかを見てみましょう。

レイアウトコンポーネント

新しいfetchを使用すると、レイアウトコンポーネントから直接API呼び出しを行うことができるようになりました。これはv2.12のリリース以前は不可能でした。

考えられるユースケース

  • Nuxtレイアウトでバックエンドから設定データを取得し、フッターとナビゲーションバーを動的に生成する
  • ナビゲーションバーでユーザー関連データ(ユーザープロファイル、ショッピングカートのアイテム数など)を取得する
  • layouts/error.vueでサイト関連データを取得する

ビルディングブロック(子/ネストされた)コンポーネント

子コンポーネントでもfetchフックが利用可能になったことで、ページレベルのコンポーネントから一部のデータフェッチタスクをオフロードし、ネストされたコンポーネントに委任できるようになりました。これはv2.12のリリース以前は不可能でした。

これにより、ルートレベルのコンポーネントの責任が大幅に軽減されます。

考えられるユースケース - 子コンポーネントにpropsを渡すことはできますが、子コンポーネントが独自のデータフェッチロジックを持つ必要がある場合、それが可能になりました!

複数のfetchフックの呼び出し順序

各コンポーネントが独自のデータフェッチロジックを持つことができるため、それぞれがどのような順序で呼び出されるか疑問に思うかもしれません。

fetchフックはサーバー側で一度(Nuxtアプリへの最初のリクエスト時)、そしてクライアント側でさらなるルートへのナビゲーション時に呼び出されます。しかし、各コンポーネントに1つのfetchフックを定義できるため、fetchフックはそれらの階層順に呼び出されます。

サーバー側でのfetchの無効化

さらに、必要に応じてサーバー側でfetchを無効にすることもできます。

export default {
  fetchOnServer: false
}

これにより、fetchフックはクライアント側でのみ呼び出されます。fetchOnServerがfalseに設定されている場合、コンポーネントがサーバー側でレンダリングされると$fetchState.pendingtrueになります。

エラーハンドリング

新しいfetchはコンポーネントレベルでエラーを処理します。その方法を見てみましょう。

データを非同期に取得するため、新しいfetch()は、リクエストが完了し、正常に処理されたかどうかを確認するための$fetchStateオブジェクトを提供します。

以下が$fetchStateオブジェクトの構造です。

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

3つのキーがあります。

  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ページのパフォーマンス向上

新しいfetchフックを使用して、:keep-alive-propsプロップとactivatedフックを利用することで、Nuxtページのコンポーネントのパフォーマンスを向上させることができます。

Nuxtは、フェッチされたデータと共に、特定の数のページをメモリにキャッシュすることを許可します。また、データを再フェッチするまでに数秒の時間差を追加することも可能です。

上記のいずれかのメソッドを機能させるには、一般的な<nuxt />および<nuxt-child>コンポーネントでkeep-aliveプロップを使用する必要があります。

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

さらに、<nuxt />コンポーネントに:keep-alive-propsを渡すことで、フェッチされたデータと共に多数のページをキャッシュできます。

:keep-alive-propsプロップを使用すると、サイト内を移動中にメモリに保持するページ数の最大数を指定できます。

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

上記はページパフォーマンスを向上させるためのより高レベルで汎用的な方法ですが、次の方法は$fetchStatetimestampプロパティを使用し、データを再フェッチするまでの秒数と比較することで、フェッチリクエストの呼び出しを最適化します。

VueのactivatedフックがNuxtのkeep-aliveプロップと組み合わせて、データを再フェッチするために使用されます。

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

ページコンポーネントに関しては、新しいfetchasyncData()と非常に似ているように見えます。どちらもローカルデータを扱いますが、以下に示すように、注目すべきいくつかの重要な違いがあります。

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` 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が大幅に異なっていたことをご存知でしょう。

これは破壊的変更ですか?

いいえ、そうではありません。実際、既存のNuxtアプリケーションで破壊的変更を避けるために、contextを最初の引数として渡すことで、古いfetchを引き続き使用できます。

v2.12のを比較したfetchフックの注目すべき変更点のリストは次のとおりです。

1. fetchフックの呼び出し順序

変更前 - fetchフックはコンポーネントの初期化前に呼び出されたため、thisはfetchフック内で利用できませんでした。

変更後 - ルートにアクセスした際に、サーバー側でコンポーネントインスタンスが作成された後にfetchが呼び出されます。

2. this vs context

変更前 - contextが最初のパラメーターとして渡されることを前提として、ページレベルのコンポーネントでNuxt contextにアクセスできました。

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

変更後 - パラメーターを渡すことなく、Vueのクライアントサイドフックと同様にthisコンテキストにアクセスできます。

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

3. fetchフックの可用性

変更前 - ページ(ルートレベル)コンポーネントのみがサーバー側でデータを取得することが許されていました。

変更後 - これで、任意のVueコンポーネントで非同期にデータをプリフェッチできるようになりました。

4. fetchフックの呼び出し順序

変更前 - fetchはサーバー側で一度(Nuxtアプリへの最初のリクエスト時)、そしてクライアント側でさらなるルートへのナビゲーション時に呼び出すことができました。

変更後 - 新しいfetchは古いfetchと同じですが…

…各コンポーネントに1つのfetchを持つことができるため、fetchフックはそれらの階層順に呼び出されます。

5. エラー処理

変更前 - API呼び出し中にエラーが発生した場合、カスタムエラーページを表示するcontext.error関数を使用していました。

変更後 - 新しいfetchは、API呼び出し中のテンプレート領域でのエラーを処理するために$fetchStateオブジェクトを使用します。

エラー処理はコンポーネントレベルで実行されます。

これは、Nuxt 2.12以前のようにカスタムエラーページをユーザーに表示できないという意味でしょうか?

はい、表示できますが、ページレベルのコンポーネントデータに関する場合はasyncData()のみです。fetchを使用する場合は、this.$nuxt.error({ statusCode: 404, message: 'Data not found' })を利用してカスタムエラーページを表示できます。

結論

新しいfetchフックは、データ取得とルートレベルおよびビルディングブロックコンポーネントの編成において、多くの改善と新たな柔軟性をもたらします!

同じルート内で複数のAPI呼び出しが必要な新しいNuxtプロジェクトを計画および設計する際には、確かに少し異なる考え方をするようになるでしょう。

この記事が、新しいfetch機能に慣れるのに役立ったことを願っています。あなたがこれを使って何を構築するかを見るのが楽しみです。