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

Vue

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

const fruitsList = require('./data/fruitsList.json')

// 〜〜〜 省略 〜〜〜

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

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

APIから取得する

Headless 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" なので、これを一緒にデプロイすれば、無効なルーティングでもリダイレクトされるようになる。