Nuxt.js でネストされたルートを使う
- 公開日:
Nuxt.js にはレイアウト機能がありますが、layouts 内の Vue テンプレートでは asyncData()
を使うことができず、payload を渡したりするのに不便です。
そこで、レイアウトの代わりにネストされたルートを使うと、この問題を解決することができます。
ネストされたルートを使う
pages フォルダ内で、フォルダと同一名の Vue ファイルを設置することで使うことができます。
ここでは、/blog/
以下のページで、カテゴリ一覧などのサイドバーを表示しておく親と、各記事を表示する子テンプレートを作ることを考えてみます。
pages/
|-- blog/
| |-- _postId/
| |-- index.vue
|-- blog.vue
|-- index.vue
このような木構造のとき、pages 直下の blog.vue は親テンプレートとなります。/blog/
以下のページにいる間はずっと表示されますが、データの取得などは初めて表示されるときだけ行なわれます。
親テンプレートは、<nuxt-child>
コンポーネントをテンプレート内に入れておく必要があります。このコンポーネントがないと、子ページの内容が表示されません。
<!-- pages/blog.vue(親テンプレート) -->
<template>
<div class="columns">
<div class="column">
<nuxt-child />
</div>
<aside class="column is-one-quarter">
<div class="menu">
<p class="menu-label">カテゴリ</p>
<ul class="menu-list mb-5">
<li v-for="categ in categs" :key="`aside-categ-${categ.id}`">
<nuxt-link :to="`/categories/${categ.id}/`" active-class="is-active">{{ categ.name }}</nuxt-link>
</li>
</ul>
</div>
</aside>
</div>
</template>
<script>
export default {
asyncData() {
// カテゴリ情報を取得する処理
return { categs }
},
}
</script>
子テンプレートでは、普通に記事の内容を表示します。親の <nuxt-child>
コンポーネントの位置に表示されます。
<!-- pages/blog/_postId/index.vue(子テンプレート) -->
<template>
<article>
<h1>{{ post.title }}</h1>
<div class="content" v-html="post.content" />
</article>
</template>
<script>
export default {
asyncData({ params }) {
// 記事情報を取得する処理
return { post }
},
}
</script>
親から子にデータを渡す
カテゴリアーカイブページを作るときなど、親が持っているデータを子で利用したい場面があります。
そのような時には、普通の Vue コンポーネントと同じように props
を通じてデータを送ることができます。
<!-- pages/blog/categories/_categId/index.vue(子テンプレート) -->
<script>
export default {
props: {
// URL から categId は分かるが、カテゴリ名は分からない……
categName: {
type: String,
default: '',
},
},
}
</script>
親では、<nuxt-child>
コンポーネントに属性を付けてデータを送ります。
<!-- pages/blog.vue(親テンプレート)-->
<template>
<nuxt-child categ-name="categName" />
</template>
<script>
export default {
asyncData() {
// カテゴリ情報を取得する処理
return { categs }
},
computed: {
categName() {
const categId = this.$route.params.categId
const categ = categId && this.categs.find(categ => categ.id === categId)
return categ
? categ.name
: null // 関係ないルート(記事詳細ページなど)で props を受け取らなくて済むように null を送る
},
},
}
</script>
子から親にデータを渡す
こちらも普通の Vue コンポーネントと同じく、イベントによってデータを渡せます。
子で this.$emit('eventname', dataIfNeeded)
して、親で <nuxt-child @eventname="listener">
と書くだけです。
子のページトランジションがうまく行かない
親の <nuxt-child>
に :key="$route.path"
を追加するとうまく行くかも知れません。
payload を渡す
npx nuxt generate
で静的サイト生成する際、ネストされたルートが使われているページでは、親と子それぞれに payload が渡ります。
payload オブジェクトの中に parent, child キーなどで別々にオブジェクトを作ると、payload の受け取りが楽です。
// nuxt.config.js
// categs にカテゴリ情報、post に記事情報が入ってるとして
export default {
generate: {
routes: [
{
route: '/blog/example-post',
payload: {
parent: { categs },
child: { post },
},
},
],
},
}
<!-- pages/blog.vue(親テンプレート) -->
<script>
export default {
asyncData({ payload, error }) {
if (payload) return payload.parent
error({ statusCode: 419, message: 'Page Expired' })
},
}
</script>
<!-- pages/blog/_postId/index.vue(子テンプレート)-->
<script>
export default {
asyncData({ payload, error }) {
if (payload) return payload.child
error({ statusCode: 419, message: 'Page Expired' })
},
}
</script>
フロントページでネストされたルートを使いたい
pages フォルダ直下に index フォルダを作ります。
pages/
|-- index/
| |-- index.vue
|-- index.vue
index ばかりで分かりにくいですが、pages/index.vue
が親テンプレート、pages/index/index.vue
が子テンプレートとなります。
他のページは index フォルダの中に作っていきます。