Nuxt.jsのgenerateコマンドで動的ルーティングページを生成する方法とpayload による動的ルーティング生成の高速化について紹介します。
また、cross-envを使ったAPIのURLを環境ごとに変える方法もあわせて紹介します。
目次
動的ルーティングの生成
動的なパラメータを用いたルートを生成する場合は、動的なルーティングの配列をroutesにセットします。
例として店舗詳細ページ(pages/shops/_id.vue)を動的に生成する場合は、以下のように店舗IDをパラメータとしてセットしていきます。
・nuxt.config.js
1 2 3 4 5 6 7 8 9 10 11 12 | export default { generate: { routes: [ '/shops/1', '/shops/2', '/shops/3' ... ] } } |
このような書き方だと、generateするタイミングに応じて店舗が増減すること考えられ、毎回generateするたびに手動で書いていくことになり非効率です。
そのため、APIから動的パラメータを取得するように実装します。
動的パラメータを取得するAPIから店舗IDを取得し、mapメソッドでパスを返していきます。
・nuxt.config.js
1 2 3 4 5 6 7 8 9 10 | generate: { routes () { return axios.get('https://example.com/api/shops') .then((res) => { return res.data.map((shopId) => { return '/shops/' + shopId }) }) } } |
動的パラメータの店舗のIDが欲しいので、pluckメソッドでIDのみ配列化して取得します。
・app/controllers/api/shops_controller.rb(API側(Rails)のコントローラ)
1 2 3 | def index render json: ::Shop.all.pluck(:id) end |
payloadによる高速化
サーバーからshop.idを利用してルーティングを生成しますが、必要なデータ以外を破棄しています。その場合は/shops/_id.vueのデータを再度取得する必要があります。
payloadにページ生成に必要なデータを格納しておくことで、店舗詳細ページ遷移時に再度データを取得せずに済みます。
・nuxt.config.js
1 2 3 4 5 6 7 8 9 10 11 | generate: { routes () { return axios.get('https://example.com/api/shops') .then((res) => { return res.data.map((shop) => { return '/shops/' + shop.id payload: shop }) }) } } |
今回は、店舗のデータが欲しいのでpluckメソッドは使わずに取得します。
・app/controllers/api/shops_controller.rb(API側(Rails)のコントローラ)
1 2 3 | def index render json: ::Shop.all end |
以下のようにpages/shops/_id.vueからpayloadにアクセスすることできます。
・pages/shops/_id.vue
1 2 3 4 5 6 7 8 9 10 11 | ... async asyncData ({ store, params, error, payload }) { if (payload) { return { shop: payload } } else { return { shop: await store.dispatch('shop/fetchShop', { id: params.id }) } } }, ... |
複数のAPIからデータをまとめてroutesにセットする場合
複数のAPIからデータを取得、まとめてroutesにセットする場合は、例として以下のように実装しました。
・app/controllers/api/shops_controller.rb
1 2 3 | def index render json: ::Shop.all end |
・app/controllers/api/companies_controller.rb
1 2 3 | def index render json: ::Company.all end |
・app/controllers/api/members_controller.rb
1 2 3 | def index render json: ::Member.all end |
・nuxt.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | generate: { async routes () { const generates = [] await axios.get(`${envSet.API_URL}/api/shops/`) .then((res) => { res.data.map((shop) => { generates.push({ route: 'shops/' + shop.id, payload: shop }) }) }) await axios.get(`${envSet.API_URL}/api/companies/`) .then((res) => { res.data.map((city) => { generates.push({ route: 'companies/' + city.id, payload: city }) }) }) await axios.get(`${envSet.API_URL}/api/stations/`) .then((res) => { res.data.map((station) => { generates.push({ route: 'stations/' + station.id, payload: station }) }) }) return generates } } |
payloadに格納するデータが複雑な場合
Railsの場合ですが、payloadのデータが::Shop.allみたいにシンプルに取得できない場合(データを整形する必要がある場合など)は、例として以下のようにしてJSONデータを返すことができます。
・app/controllers/api/shops_controller.rb
1 2 3 4 | def index @shops = ::Shop.all render 'index', formats: 'json', handlers: 'jbuilder' end |
・app/views/api/shops/index.json.jbuilder
1 2 3 4 | json.array! @shops do |shop| json.merge! shop.attributes json.company_shop_name shop&.company&.name + ' ' + shop.name end |
APIのURLを環境ごとに変えたい場合
現状だとAPIのURLが固定になっているため、環境ごとに変えたい場合はcross-envを使います。
詳しいインストール方法などは以下の記事に書いてあるので参考にしてください。
参考:Nuxtでcross-envを使い環境ごとに環境変数を分ける
以下のように環境ごとのAPIのURLを用意します。
・env.development.js
1 2 3 | module.exports = { API_URL: 'http://localhost:7000' } |
・env.staging.js
1 2 3 | module.exports = { API_URL: 'https://stg.example.com' } |
・env.production.js
1 2 3 | module.exports = { API_URL: 'https://example.com' } |
package.jsonのscriptsに環境ごとのgenerateコマンドを追加します。
・package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 | { ... "scripts": { "generate:dev": "cross-env NODE_ENV=\"development\" nuxt generate", "generate:stg": "cross-env NODE_ENV=\"staging\" nuxt generate", "generate:prod": "cross-env NODE_ENV=\"production\" nuxt generate" } ... } |
次に叩いたgenerateコマンドから、process.env.NODE_ENVによって適用環境を取得します。
envSet.API_URLで適用された環境のAPIのURLを取得します。
・nuxt.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const environment = process.env.NODE_ENV || 'development' const envSet = require(`./env.${environment}.js` ... generate: { routes () { return axios.get('${envSet.API_URL}/api/shops) .then((res) => { return res.data.map((shop) => { return '/shops/' + shop.id payload: shop }) }) } } ... |
存在しないページのハンドリング
静的ページとして生成してほしくない場合、fallbackをtrueにすることで、ファイルが存在しな場合は404.htmlとなります。
layoutsディレクトリ配下にerror.vueを作成することでエラーページ(200.html)が生成されますが、fallbackが未設定の場合はこのエラーページ(200.html)に飛ばされます。
・nuxt.config.js
1 2 3 4 5 | export default { generate: { fallback: true } } |
以上、Nuxt.jsのgenerateコマンドで動的ルーティングページを生成する方法とpayload による動的ルーティング生成の高速化について紹介しました。