API: fetch メソッド

Nuxt >= 2.12

Nuxt.js v2.12 では、あらゆる Vue コンポーネントfetch という新しいフックが導入されています。

ライブデモコード例を見てください。

fetch(context) は非推奨になりましたが、代わりに無名ミドルウェアをページ内の middleware(context) で使用できます。

fetch を使うタイミングは?

非同期データ取得の必要がある度です。fetch はサーバーサイドでルートをレンダリングするときに呼び出され、クライアントサイドでは遷移するときに呼び出されます。

コンポーネントレベルで $fetchState を公開します:

  • $fetchState.pending: Booleanfetchクライアントサイドで呼び出されたときにプレースホルダを示します。
  • $fetchState.error: null または Error。エラーメッセージを示します。
  • $fetchState.timestamp: Integer。最後に fetch したタイムスタンプです。keep-alive でのキャッシングに便利です。

テンプレートから fetch フックを呼び出すには、以下のように $fetch() を使用します:

<button @click="$fetch">Refresh</button>

コンポーネントメソッドでは以下のとおりです:

// スクリプトセクションのコンポーネントメソッドより
export default {
  methods: {
    refresh() {
      this.$fetch();
    }
  }
};

fetch フック内では this.$nuxt.context を使用して、Nuxt context にアクセスできます。

オプション

  • fetchOnServer: Boolean または Function(デフォルト: true)。サーバーがページをレンダリングする際に fetch() を呼び出します。
  • fetchDelay: Integer(デフォルト: 200)。最小実行時間をミリ秒単位で設定します(過剰実行を防ぐため)。

fetchOnServer がファルシー(false または false を返す)な場合、fetch はクライアントサイドでのみ呼び出され、サーバでコンポーネントをレンダリングする際には $fetchState.pendingtrue を返します。

<script>
export default {
  data () {
    return {
      posts: []
    }
  },
  async fetch () {
    this.posts = await this.$http.$get('https://jsonplaceholder.typicode.com/posts')
  },
  fetchOnServer: false
}
</script>

今回は公式の http モジュールを使って HTTP リクエストを行ってみます。

投稿記事を掲載したブログをホームページに持ちましょう:

pages/index.vue

<template>
  <div>
    <h1>Blog posts</h1>
    <p v-if="$fetchState.pending">Fetching posts...</p>
    <p v-else-if="$fetchState.error">Error while fetching posts: {{ $fetchState.error.message }}</p>
    <ul v-else>
      <li v-for="post of posts" :key="post.id">
        <n-link :to="`/posts/${post.id}`">{{ post.title }}</n-link>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data () {
    return {
      posts: []
    }
  },
  async fetch () {
    this.posts = await this.$http.$get('https://jsonplaceholder.typicode.com/posts')
  }
}
</script>

あなたが直接 http://localhost:3000/ にアクセスすると、サーバーレンダリング(SEO に効果があります)されている完全な投稿リストを見ることができます。

Screenshot 2019-03-11 at 23 04 57

Nuxt は、fetch の中でどのようなデータを変化させたかをうまく検出し、返された HTML に含まれる JSON を最適化します。

では、pages/posts/_id.vue ページを追加して、/posts/:id に投稿を表示させてみましょう。

pages/posts/_id.vue

<template>
  <div v-if="$fetchState.pending">Fetching post #{{$route.params.id}}...</div>
  <div v-else>
    <h1>{{ post.title }}</h1>
    <pre>{{ post.body }}</pre>
    <p><n-link to="/">Back to posts</n-link></p>
  </div>
</template>

<script>
export default {
  data () {
    return {
      post: {}
    }
  },
  async fetch() {
    this.post = await this.$http.$get(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
  }
}
</script>

遷移すると、クライアントサイドに "Loading post #..." が表示され、投稿の更新時には "Loading post #..." の表示はありません(ブラウザ上ではハードリフレッシュ)。

fetch-nuxt3

fetch フックを含むコンポーネントの場合、this.$fetch() にアクセスして fetch フックを再呼び出しします($fetchState.pending は再び true になります)。

クエリ文字列の変化のリスニング

デフォルトでは、クエリ文字列の変更で fetch フックは呼び出されません。クエリの変更を監視するには、ウォッチャに $route.query を追加して $fetch を呼びます:

export default {
  watch: {
    '$route.query': '$fetch'
  },
  async fetch() {
    // クエリの変更時にも呼び出される
  }
}

キャッシング

<nuxt/><nuxt-child/> コンポーネントで keep-alive ディレクティブを使うと、既に訪問したページの fetch 呼び出しを保存することができます:

layouts/default.vue

<template>
  <nuxt keep-alive />
</template>

また、<nuxt> コンポーネントに prop の keep-alive-props を渡すことで、<keep-alive> に渡す props を指定することもできます。
例: <nuxt keep-alive :keep-alive-props="{ max: 10 }" /> ページコンポーネントを 10 ページ分だけメモリに保持するようにします。

activated フックを使う

Nuxt は、最後に fetch を呼び出したときの this.$fetchState.timestamp(タイムスタンプ)を直接付与します(SSR を含む)。このプロパティを activated フックと組み合わせることで、fetch に30秒のキャッシュを追加することができます。

pages/posts/_id.vue

<template>
  ...
</template>

<script>
export default {
  data () {
    return {
      post: {}
    }
  },
  activated() {
    // 最後の fetch から30秒以上経っていれば、fetch を呼び出します
    if (this.$fetchState.timestamp <= (Date.now() - 30000)) {
      this.$fetch()
    }
  },
  async fetch() {
    this.post = await this.$http.$get(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`)
  }
}
</script>

最後の fetch 呼び出しが30秒以内であれば、同じページへの遷移で fetch は呼ばれません。

fetch-keep-alive-nuxt

Nuxt <= 2.11

fetch メソッドは、ページがレンダリングされる前に、データをストアに入れるために使われます。コンポーネントのデータをセットしないという点を除いては asyncData メソッドとよく似ています。

  • 型: Function

fetch メソッドが設定されている場合、コンポーネント(ページコンポーネントに限ります)がロードされる前に毎回呼び出されます。サーバサイドでは一度だけ呼び出され(Nuxt アプリケーションへの最初のリクエスト時)、クライアントサイドでは他のルートへ移動したときに呼び出されます。

fetch メソッドは第一引数として context オブジェクトを受け取るので、データを取得し、取得したデータをストアに入れることができます。fetch メソッドを非同期にするためには Promise を返却してください。そうすれば Nuxt.js はコンポーネントがレンダリングされる前に promise が解決されるまで待機します。

警告: fetch メソッド内では、コンポーネントがインスタンス化される前に呼び出されるため、this を通してコンポーネントのインスタンスにアクセスすることはできません

pages/index.vue の例:

<template>
  <h1>Stars: {{ $store.state.stars }}</h1>
</template>

<script>
export default {
  fetch ({ store, params }) {
    return axios.get('http://my-api/stars')
    .then((res) => {
      store.commit('setStars', res.data)
    })
  }
}
</script>

async/await を使ってコードをスッキリさせることもできます:

<template>
  <h1>Stars: {{ $store.state.stars }}</h1>
</template>

<script>
export default {
  async fetch ({ store, params }) {
    let { data } = await axios.get('http://my-api/stars')
    store.commit('setStars', data)
  }
}
</script>

Vuex

ストアアクションを呼び出す場合は、fetch 内の store.dispatch を使用してください。その際、内部の async/await を用いてアクションの終了を待つようにしてください:

<script>
export default {
  async fetch ({ store, params }) {
    await store.dispatch('GET_STARS');
  }
}
</script>

store/index.js

// ...
export const actions = {
  async GET_STARS ({ commit }) {
    const { data } = await axios.get('http://my-api/stars')
    commit('SET_STARS', data)
  }
}

クエリ文字列の変化のリスニング

fetch メソッドは、デフォルトではクエリ文字列の変更に対して呼び出されません。たとえばページネーション用のコンポーネントを作成する時など、この挙動を変更したい場合は、ページコンポーネントの watchQuery プロパティを使用してリスニング用のパラメータを設定することできます。詳しくは、API watchQuery のページを参照してください。

asyncData head

Contributors

間違いを見つけた、またはドキュメントに貢献したいですか? GitHub でこのページを編集する

Platinum Sponsors

Storyblok Support Us