Nuxt Nation カンファレンスが開催されます。11月12日~13日にご参加ください。
リリース·  

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

fetchフックのさまざまな機能を探り、Nuxtアプリケーションにデータを取り込むための全く新しい方法を学びましょう。

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

この記事では、fetchフックのさまざまな機能を探り、その仕組みを理解していきます。

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

Nuxtライフサイクルフックに関して、fetchcreatedフックの後のVueライフサイクル内に位置します。ご存知のように、すべてのVueライフサイクルフックは、それぞれのthisコンテキストで呼び出されます。fetchフックにも同じことが当てはまります。

New fetch in Nuxt lifecycle

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

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フックは、サーバーサイドで1回(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をチェックすることでサーバーサイドでHTTPステータスコードを設定し、throw new Error()ステートメントでフォローアップできます。

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 }" />

上記は、ページのパフォーマンスを向上させる1つの方法であり、より高レベルで汎用的です。次の方法は、$fetchStatetimestampプロパティを使用して、データを再フェッチするまでの秒数の遅延と比較することで、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と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フックはコンポーネントの初期化前に呼び出されたため、fetchフック内でthisは使用できませんでした。

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

2. thiscontext

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

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

**後 -** パラメーターを渡さずに、Vueクライアントサイドフックと同様にthisコンテキストにアクセスできます。

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

3. fetchフックの可用性

**前 -** サーバーサイドでデータを取得できるのは、ページ(ルートレベル)コンポーネントのみでした。

**後 -** あらゆるVueコンポーネントでデータを非同期にプリフェッチできるようになりました。

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

**前 -** fetchは、サーバーサイドで1回(Nuxtアプリへの最初のリクエスト時)、クライアントサイドでさらにルートに移動するときに呼び出すことができました。

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

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

5. エラー処理

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

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

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

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

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

結論

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

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

この記事が、新しいfetch機能を理解するのに役立ったことを願っています。皆さんがそれで何を構築するのか、楽しみにしています。

← ブログに戻る