【Nuxt.js】middlewareを活用しブログ記事取得のパフォーマンスを改善する
  • 2019.10.08に公開
  • 2019.10.12に更新
  • ブログ構築
  • 5. ブログ記事周りの構築
  • No.4 / 4
「ブログ構築」では、Nuxt.js v2.14未満が使用されています。 Nuxt.jsはv2.13~14で大きなバージョンアップがありました。それに伴い、書き方も大きく変化しています。 v2.14以上で書かれた「ブログ構築」の続編は、カテゴリー「 ブログ構築TS 」 で公開しています。

今回達成すること

さて、今回はmiddlewareを使って、Contentfulからブログ記事を取得していきます。

middlewareを使うことで、Contentfulへリクエストを送信する頻度を減らし、サイトのパフォーマンスを最適にします。

現状の問題点

現在、記事一覧をpages/index.vueで取得しています。

現在のpages/index.vue
<script>

export default {

  async asyncData({ env }) {
    let posts = []
    await client.getEntries({
      content_type: env.CTF_BLOG_POST_TYPE_ID,
      order: '-fields.publishDate'
    }).then(res => (posts = res.items)).catch(console.error)
    return { posts }
  }
}
</script>

このままだと、index.vueを経由しないと記事一覧を取得できません。

例えば、記事ページで「関連記事」や「人気の記事」を表示したいとなった場合、ページ毎にContentfulへリクエストを送信しなければなりません。

images orignal.001

これではパフォーマンスが悪すぎます。

middlewareを使った解決策

そこで、APIリクエストを最初の一回だけにする方法として、 middleware を活用します。

middlewareとは、特定のページ又は全てのページにおいて、ページがレタリングされる前に実行する関数を定義する、Nuxt.jsの機能の一つです。

ここに関数を仕込んでおくと、どのページから来たとしても、必ず記事一覧を取得することができます。

流れとしては、こんな感じです。

  1. middlewareからContentfulへリクエスト
  2. Vuexに保管
  3. 必要な記事をJavascriptで取得

images orignal.002

コードの編集に移りましょう。まずは、index.vueから

まずは、pages/index.vueのscript内のコードを整理します。

linkTo算出プロパティと、asyncData自体をまるっと削除してください。

pages/index.vue
<script>

// import client from '~/plugins/contentful'  削除

export default {

  computed: {

 // 削除
 // linkTo: () => (obj) => {
 //   return { name: 'posts-slug', params: { slug: obj.fields.slug } }
 // }
    
  },
 // 削除
 //  async asyncData({ env }) {
 //  let posts = []
 //  await client.getEntries({
 //    content_type: env.CTF_BLOG_POST_TYPE_ID,
 //    order: '-fields.publishDate'
 //  }).then(res => (posts = res.items)).catch(console.error)
 //  return { posts }
 // }
}
</script>

そして、index.jsへ削除したコードを移動します。

store/index.js
import client from '~/plugins/contentful'	// 追記

// 追記
export const state = () => ({
  posts: []
})

export const getters = {

	// 追記
  linkTo: () => (name, obj) => {
    return { name: `${name}-slug`, params: { slug: obj.fields.slug } }
  }
}

// 追記
export const mutations = {
  setPosts(state, payload) {
    state.posts = payload
  }

}

// 追記
export const actions = {
  async getPosts({ commit }) {
    await client.getEntries({
      content_type: process.env.CTF_BLOG_POST_TYPE_ID,
      order: '-fields.publishDate' // desc
    }).then(res =>
      commit('setPosts', res.items)
    ).catch(console.error)
  }

}

  • Vuexストアではモジュールモードの書き方を採用しています。

    Vuex ストア #モジュールモード

    ほかに「クラシックモード」の書き方がありますが、Nuxtjs v3 以降で廃止予定のため推奨しません。

    Vuex ストア #クラシックモード

  • actions経由でContentful APIを取得しています。

    mutations内でContentful APIを取得することも可能ですが、非同期処理で実行するために、actionsを経由しています。

    非同期処理とは、「ある関数が呼び出されたとき、戻り値として本来渡したい結果を返すのではなく、一度関数としては終了し(=呼び出し元に戻る)、後で『本来渡したかった値』を返せる状態になったときに、呼び出し元にその値を通知する」処理方法です。

    引用(ここ分かりやすいよ!後で読んでみてください)

    非同期処理ってどういうこと?JavaScriptで一から学ぶ - Qiita

  • order: '-fields.publishDate'

    Contentful APIを呼び出す時、並び順を指定することができます。

    そのプロパティがorderです。

    今回は、公開日(publishDate)が新しい順に記事を取得しています。

    「-」を外すと、公開日が古い順に記事を取得することができます。

    • 「-」あり ... 降順
    • 「-」なし ... 昇順
  • linkTo: () => (name, obj) => ...

    linkTo算出プロパティは、他のオブジェクトでも使えるようにnameの引数を追加しました。

    これにより、カテゴリーページやタグページに使い回しすることができます。

算出プロパティ 生成されるPath
記事の場合 linkTo('posts', post) "posts/slug"
カテゴリーの場合 linkTo('categories', category) "categories/slug"
タグの場合 linkTo('tags', tag) "tags/slug"

middlewareからindex.jsを実行する

それではmiddlewareにファイルを作成し、index.js内のコードを実行します。

middleware内にgetContentful.jsを作成してください。

$ touch middleware/getContentful.js

そして以下のように編集します。

middleware/getContentful.js
export default async ({ store }) => {
  if (!store.state.posts.length) await store.dispatch('getPosts')
}

middlewareは全てのページで実行される

基本的に、middlewareは全てのページを表示する前に実行されます。

そこで、記事が存在しないときだけリクエストを投げるように、if (!store.state.posts.length)という条件分岐をしています。

これにより、Vuex内のposts配列が空っぽの場合のみ、リクエストを投げることになります。

nuxt.config.jsにmiddlewareを登録する

今のままでは、middleware内の関数は実行されません。

nuxt.config.jsに登録する必要があります。

nuxt.config.js
export default {

  // 追記
  router: {
    middleware: [
      'getContentful'
    ]
  },
  // 追記終了
  
  axios: {},
}
  • ruoterプロパティのmiddlewareに配列を渡します。

    ここには、middlewareディレクトリ内のファイル名を配列で渡します。

以上で、middlewareの登録は完了です。

index.vueから呼び出してみる

さて、Vuexに保管した記事一覧をindex.vueで呼び出しましょう。

pages/index.vue
<script>
// import { mapGetters } from 'vuex'        // 削除
import { mapState, mapGetters } from 'vuex' // 追記

export default {

  computed: {
    ...mapState(['posts']),                               // 追記
//    ...mapGetters(['setEyeCatch', 'draftChip'])         // 削除
    ...mapGetters(['setEyeCatch', 'draftChip', 'linkTo']) // 追記
  }
}
</script>

index.vueに記事一覧が表示されていますか?

表示されていれば成功です。

さあ、次へ進みましょう。

単一記事の取得方法を変更する

次は、pages/posts/_slug.vueを編集します。

今現在の単一記事の取得方法は、

  • URLに紐付けたパラメーターのslugを、
  • Contentfulへ渡して検索し、
  • 単一記事を返しています。

これを、Vuexのpostsから取得するように変更します。

毎回APIを投げるよりは、パフォーマンスが良くなります。

pages/posts/_slug.vue
<script>
// import client from '~/plugins/contentful'  // 削除

export default {

  computed: {
    ...mapGetters(['setEyeCatch'])
  },
// 削除
//  async asyncData({ env, params }) {
//   let currentPost = null
//   await client.getEntries({
//     content_type: env.CTF_BLOG_POST_TYPE_ID,
//     'fields.slug': params.slug
//   }).then(res => (currentPost = res.items[0])).catch(console.error)
//
//   return { currentPost }
// }
  
  // 追記
  async asyncData({ payload, store, params, error }) {
    const currentPost = payload || await store.state.posts.find(post => post.fields.slug === params.slug)

    if (currentPost) {
      return { currentPost }
    } else {
      return error({ statusCode: 400 })
    }
  }
}
</script>
  • const currentPost = payload || ...

    本番環境では、payloadに記事のオブジェクトが入っているので、currentPostへ代入しています。

    payloadについては、この記事で解説しています。

    Nuxt.jsのgenerateプロパティに動的なルーティングを追加する

  • await store.state.posts.find(post => post.fields.slug === params.slug)

    payloadの値がない場合に、Vuexのpostsからfindメソッドで記事を検索します。

    渡されたslugが一致している記事を取り出しています。

  • return error({ statusCode: 400 })

    asyncDataの引数errorは、エラーコードを指定することができます。

    記事(currentPost)がない場合に、error 400を返します。

これで全ての設定が完了しました。

pushして本番環境でも確認しよう

だいぶコードがスッキリしましたね。

それでは、今までの変更をpushして本番環境でも確認してみましょう。

今回は、新規ファイルを追加したのでget add -Aを実行します。

$ git add -A
$ git commit -m "add_middleware_by_getContentful.js"
$ git push

本番環境でも表示されましたか?

OK!ナイスですね!

まとめ

今回は、middlewareを使って記事一覧を取得しました。

最後にもう一度流れを整理しておきますね。

  1. middlewareディレクトリに getContentful.js という名のファイルを作成し、
  2. nuxt.config.jsrouterプロパティに、middlewareを登録する。
  3. getContentful.jsでは、index.jsactions内でContentfulへリクエストを投げ、
  4. 帰ってきた記事一覧を、index.jspostsで保管する。
  5. index.vue では、index.jsのpostsを参照し、記事一覧を表示する。
  6. posts/_slug.vue では、パラメーターが一致している記事を取り出す。

今回説明したことは、以上となります。

お疲れ様でした。

さて、次回は?

これで、ブログ記事周りの構築は完了です。

次は、カテゴリーモデルを新たに作成して、カテゴリー別の記事表示を実現していきましょう。

またね!

あなたの力になれること
私自身が独学でプログラミングを勉強してきたので、一人で学び続ける苦しみは痛いほど分かります。そこで、当時の私がこんなのあったら良いのにな、と思っていたサービスを立ち上げました。周りに質問できる人がいない、答えの調べ方が分からない、ここを聞きたいだけなのにスクールは高額すぎる。そんな方に向けた単発・短期間メンターサービスを行っています。
独学プログラマのサービス
ブログ構築の投稿
1
  • 更新情報
  • /
  • #01
ブログ構築カテゴリーの記事修正、更新情報【2020/05/19追記: このカテゴリーの更新を一旦終了といたします】
2
  • 更新情報
  • /
  • #02
Nuxt.js v2.13.0新機能メモの公開とv2.12.2にダウングレードする方法
1
  • 今回作るアプリケーション
  • /
  • #01
Nuxt.jsとContentfulで作るマイブログ
1
  • 開発環境にNuxt.jsを立ち上げる
  • /
  • #01
Nuxt.jsを動かす環境を構築する
2
  • 開発環境にNuxt.jsを立ち上げる
  • /
  • #02
Nuxt.jsのプロジェクトを作成する
3
  • 開発環境にNuxt.jsを立ち上げる
  • /
  • #03
Hello Nuxtを表示する
1
  • Nuxt.jsアプリを公開する
  • /
  • #01
Nuxt.jsをデプロイする前の事前準備を行う
2
  • Nuxt.jsアプリを公開する
  • /
  • #02
Netlifyの初期セットアップとNuxt.jsのデプロイを行う
3
  • Nuxt.jsアプリを公開する
  • /
  • #03
NetlifyにデプロイしたNuxt.jsに独自ドメインを設定する
1
  • Contentfulのセットアップ
  • /
  • #01
【Nuxt.js Universal】Vuetify2.0にバージョンアップしよう
2
  • Contentfulのセットアップ
  • /
  • #02
【画像で説明】Contentfulの使い方。初期設定と各メニューについて学ぶ
3
  • Contentfulのセットアップ
  • /
  • #03
Contentfulにブログ記事モデルを作成していこう
4
  • Contentfulのセットアップ
  • /
  • #04
ContentfulからAPIを取得してNuxt.jsで記事一覧を表示する
1
  • ブログ記事周りの構築
  • /
  • #01
Nuxt.jsにContentfulのブログ記事を表示する
2
  • ブログ記事周りの構築
  • /
  • #02
Contentfulから取得した下書き記事を開発環境に表示する
3
  • ブログ記事周りの構築
  • /
  • #03
Nuxt.jsのgenerateプロパティに動的なルーティングを追加する
4
  • ブログ記事周りの構築
  • /
  • #04
【Nuxt.js】middlewareを活用しブログ記事取得のパフォーマンスを改善する
1
  • カテゴリーページの構築
  • /
  • #01
【Contentful】カテゴリーモデルとブログ記事モデルの関連付け
2
  • カテゴリーページの構築
  • /
  • #02
【Nuxt.js × Contentful】ブログ記事に関連付くカテゴリーを表示する
3
  • カテゴリーページの構築
  • /
  • #03
【Nuxt.js × Contentful】カテゴリー記事一覧ページを作成する
1
  • タグ機能の構築
  • /
  • #01
Contentfulにタグモデルを作成し関連付けを行う
2
  • タグ機能の構築
  • /
  • #02
【Nuxt.js × Contentful】タグに関連付いたブログ記事を表示する
3
  • タグ機能の構築
  • /
  • #03
Contentfulのincludesを使って関連モデルを取得しタグ一覧ページを作成する
4
  • タグ機能の構築
  • /
  • #04
Vuetify2のdata-tableの使い方を学んで、タグ一覧ページをレイアウト
1
  • Nuxt.jsブログカスタマイズ
  • /
  • #01
Twitterシェアボタン、フォローボタンの作り方【Nuxt.js Universalモード編】
2
  • Nuxt.jsブログカスタマイズ
  • /
  • #02
Contentfulの全文検索を使ったNuxt.jsブログ内検索の実装
独学プログラマ
独学でも、ここまでできるってよ。
CONTACT
Nuxt.js制作のご依頼は下記メールアドレスまでお送りください。