nuxt generateの動的なルーティングの書き出し

Nuxt.jsの動的なルーティングの静的ページ書き出しというか、
routes()がいまいちよくわかってなかったのでメモ。

サンプルページ
サンプルリポジトリ

サンプルページでは/fruits/配下に_item.vueを設置して、動的なルーティングにした。
その中で「/fruits/apple/」「/fruits/orange/」「/fruits/banana」の3つを静的ページとして新たに書き出し、直接アクセスできるようにした。
また、404ページも作成した。

静的書き出し

API: generate プロパティ - Nuxt.js

Nuxt.jsには「nuxt generate」という機能があって、pages配下に置いた.vueファイルを、静的なコンテンツとして出力してくれる。
でも、動的なルーティングは無視してしまう。

吐き出されたindex.htmlから、リンク(nuxt-link)を通して動的なルーティングにアクセスすることは可能だけど、それらはindex.htmlから切り替えたものなので、「fruits/orange/」などに直接アクセスしようとしても、indexページが存在しないためアクセスできない。

routes()について

書き出しの設定は、nuxt.config.js内にあるgenerateで行うことができる。
その中で、動的なルーティングを書き出すための設定もある。

ルート / のみが Nuxt.js によって生成されます。動的なパラメーターを用いたルートを生成させたい場合は、動的なルーティングの配列をセットする必要があります。

動的なルーティングの配列は、routesという項目に追加する。
「/fruits/apple/」「/fruits/orange/」「/fruits/banana/」の3つを生成する場合、

nuxt.config.js
module.exports = {
  generate: {
    routes: [
      '/fruits/apple',
      '/fruits/orange',
      '/fruits/banana'
    ]
  }
}

となる。
けど手動で追加していくのは無理がある。

そこでroutesの部分を関数にする。

nuxt.config.js
const fruitsList = require('./data/fruitsList.json')

// 〜〜〜 省略 〜〜〜

generate: {
  routes() {
    return fruitsList.items.map(item => {
      return `fruits/${item.id}`
    })
  }
}

アイテム一覧をjsonなどから取得し、ページ名とパスをくっつけて格納した配列を作成するだけ。

APIから取得する

ヘッドレスCMSなどで、APIから取得する場合も同じ。
例えばContentful API(とSDK)を用いて、「/pages/posts/_slug.vue」の動的なルーティングを書き出しする際の設定はこんな感じになる。

nuxt.config.js
generate: {
  routes() {
    return client.getEntries({
      'content_type': 'blog',
    }).then(entries => {
      return entries.items.map(entry => {
        return `posts/${entry.fields.slug}`
      })
    })
  }
},

でもこの方法には問題がある。
「APIから叩いた値を配列に入れる」のと、「/pages/posts/_slug.vueで値を参照してAPIを叩く」で同じAPIを何度も叩くことになるため。 そこで、entryの値をpayloadとして一緒に返す。

nuxt.config.js
generate: {
  routes() {
    return client.getEntries({
      'content_type': 'blog',
    }).then(entries => {
      return entries.items.map(entry => {        return {          route: `posts/${entry.fields.slug}`,          payload: entry        }      })    })
  }
},

すると、書き出し時、/posts/_slug.vueにpayloadが渡される。 あとはasyncDataの引数に「payload」を追加すればOK。 静的書き出し時のみに渡されるので、ある時とない時で処理を分ける必要がある。

pages/page.vue
async asyncData({ route, app, store, payload }) {
  if (payload) {
    const item = payload // entryの値が入ってる
    return item
  }
  /* 以下、静的書き出しじゃない時の処理 */
}

404ページを出力

Nuxt.jsは、layoutsフォルダに「error.vue」を作成することでエラーページが作れる。
そのファイルは静的書き出しの時に「200.html」として生成されるけど、generatesオプションの「fallback」をtrueか'404.html'にすることで、かわりに404.htmlを生成することができる。

nuxt.config.js
generate: {
  fallback: true,
  routes() { // 以下略

firebaseのデフォルトは404.htmlなので、これを一緒にデプロイすれば、無効なルーティングでもリダイレクトされるようになる。

めちゃくちゃ難しく考えていたのでスッキリしました。