【Nuxt.js】generate時の余計なWebAPI通信を減らしたい

  • Web
  • NuxtJS
  • 効率化

余計な通信?

Nuxt.jsでページの静的ファイルを生成する際(generate)に、動的なルート(/blog/_id などのパラメーターを用いたルートを指す)は事前にWeb API等で解決しておかなければなりません。

generate時にはページ内の処理が実行されるので、例えば、asyncDataでデータをWeb APIで取得している場合は生成の度に通信が発生します。

つまり、動的ルーティングを解決する時に全記事の情報を取得するために通信が発生し、その後ページで静的ファイルを生成する時に通信が発生するということになってしまいます。

また、ミドルウェアは生成の度に処理されるため、通信が伴う処理を記述している場合は合計の通信回数が多くなってしまいます。

こうなってしまうことを避けるために、payloadを用いて生成時間の短縮を図る工夫がドキュメントで紹介されています。

実際、どうすればよいの?

generate時に、何度もWebAPI通信が発生してしまうことを減らしたい場合は、次の点を考えてみましょう。

  • 事前に必要なデータを全て取得しておく
  • 事前に取得しておいたデータを使い回す

実際の実装方法は、簡単なものだと公式サイトにあるドキュメントにあるようにnuxt.config.jsgenerate.routesにAPI通信&payloadを渡す処理を設定するものです。

次に示すのは、middlewareで取得するデータと記事で取得するデータを事前に取得しておいてpayloadで渡す場合の例です。

// 例
// nuxt.config.js
export default {
  /* ~略~ */
  router: {
    middleware: 'example',
  },
  generate: {
    routes: async (callback) => {
      // API通信する関数は仮としておきます
      const middlewareData = await getMiddlewareData()
      const posts = await getPosts()
      const routes = posts.map((post) => {
        return {
          route: `/blog/${content.id}`,
          payload: { middlewareData, post },
        }
      })
      callback(null, routes)
    },
  },
}
// 例
// middleware/example.js
export default async ({ store, payload }) => {
  // すでにデータがある場合はスキップ
  if (!store.getters['example/isEmpty']) {
    return
  }

  let data
  if (payload && payload.middlewareData) {
    // payloadある場合
    data = payload.middlewareData
  } else {
    // payloadない場合
    // API通信する関数は仮としておきます
    data = await getMiddlewareData()
  }

  store.commit('example/set', data)
}
// 例
// pages/blog/_id.vue の <script>
export default {
  async asyncData({ params, payload }) {
    let content
    if (payload && payload.post) {
      content = payload.post
    } else {
      // API通信する関数は仮としておきます
      content = await getSinglePost(params.id)
    }
    return { content }
  },
}

モジュールを作ってみる

プロジェクトの規模や仕様によっては、処理の実装が膨らんでしまうかと思います。そういった場合はモジュールを作成してみるのも手だと個人的には思います。

例えば、プロジェクトにmodulesディレクトリを作成します。

modules
└── prepare-dynamic-routes
    ├── helper.js (任意 : データを取得する関数等を定義しておくファイル)
    └── index.js

nuxt.config.jsで利用できるように設定します。

// nuxt.config.js
export default {
  buildModules: [
    '@/modules/prepare-dynamic-routes',
  ],
}

モジュールの内容は、例えば次のようになります。

// 例
// modules/prepare-dynamic-routes/index.js
module.exports = function () {
  // generateコマンド実行時以外はスキップ.
  if (!this.options._generate) return
  // 追記: Nuxt.js v2.14以降は以下で記述.
  if (!this.options._legacyGenerate) return

  const { getPosts, getTags } = require('./helper')

  // ビルドが始まる前に実行される処理を定義.
  // https://ja.nuxtjs.org/api/internals-builder
  this.nuxt.hook('build:before', async (builder) => {
    const logger = builder.nuxt._logger

    // データ取得.
    logger.info('Loading posts')
    const posts = await getPosts()
    logger.success(`Loaded ${posts.length} posts`)
    logger.info('Loading tags')
    const tags = await getTags()
    logger.success(`Loaded ${tags.length} tags`)

    // generate.routesを設定.
    this.options.generate.routes = posts.map((post) => {
      return {
        route: `/blog/${post.id}`,
        payload: { tags, post },
      }
    })
    logger.success('Set generate.routes')
  })
}

実際に使っているものは上記とは異なりますが、雰囲気は掴めるかと思います。

ご参考になれば幸いです。

追記

[2020/7/29] Nuxt.js v2.14.0でも動くように一部修正

この記事を共有

アバター

K.Utsunomiya
男・20代
主にWebフロントエンド技術と気になった音楽について投稿していきます。
最近ハマっていることは、クロスバイクで走ることとジムでの運動です。
詳しいプロフィール

© 2020–2021 コレ棚