Vueのコンポーネントを利用して、SNSでよくあるような、コメントに返信がぶら下がり、
さらにその返信への返信がぶら下がり...といったような一覧を作ってみました。
以下のようなイメージです。
環境
今回はNuxt.js
を使っています。
- Node.js => v10.8.0
- nuxt-edge => v2.0.0-25599455.3a0f094 (すごく中途半端なバージョンですみません)
構成
root
├ pages
│ └ comments.vue
├ components
│ └ MyComment.vue
│ ...
pages/comments.vue
まずコンポーネントを呼び出す側のページです。
<my-comment>
が再帰的なコメントコンポーネントになります。
<template>
<section>
<my-comment
:comment="comment"
/>
</section>
</template>
<script>
import MyComment from '~/components/MyComment'
export default {
components: {
MyComment
},
data() {
return {
comment: {
id: 0,
content: '親コメント',
replies: [
{
id: 1,
content: '子コメント1',
replies: []
},
{
content: '子コメント2',
replies: [
{
id: 2,
content: '孫コメント1',
replies: [
{
id: 4,
content: '曾孫コメント1',
replies: []
},
{
id: 5,
content: '曾孫コメント2',
replies: []
}
]
},
{
id: 3,
content: '孫コメント2',
replies: []
}
]
}
]
}
}
}
}
</script>
<style>
section {
padding: 1.5rem;
}
</style>
ポイント
コンポーネントに渡すデータを再帰的な構造で定義する
comment
オブジェクトの最小単位は以下のような形になります。
{
id: 'uniqueId',
content: 'コメント文字列',
replies: []
}
replies
の中に、comment
オブジェクトを入れ子にしていきます。
comment
オブジェクトが長さ1以上のreplies
をもつ限り、MyComment
が再帰的に呼び出され続けます。
id
は、v-for
を使って繰り返し描画する際のkey
としてユニークな値が必要なため、付与しています。
components/MyComment.vue
こちらが再帰的に呼び出されるコンポーネントです。
<template>
<div>
<p>{{ comment.content }}</p>
<ul v-if="comment.replies.length > 0">
<li
class="reply"
v-for="reply in comment.replies"
:key="reply.id"
>
<my-comment
:comment="reply"
/>
</li>
</ul>
</div>
</template>
<script>
import MyComment from '~/components/MyComment'
export default {
name: 'my-comment', // or myComment / MyComment
components: {
MyComment
},
props: {
comment: {
type: Object,
required: true
}
}
}
</script>
<style>
@charset 'utf8';
.reply {
display: block;
margin-left: 2rem;
margin-top: 1rem;
}
</style>
ポイント
コンポーネントにname
オプションを定義する
引用: API — Vue.js オプション / その他 #name
テンプレート内でのコンポーネント自身の再帰呼び出しを許可します。コンポーネントは Vue.component() でグローバルに登録され、グローバル ID はその名前に自動的に設定される事に注意してください。
とあります。
name
に指定した名前でコンポーネントが登録されるようです。
名前は、ケバブケース
、キャメルケース
、アッパーキャメルケース
どれでもいいようです。
コンポーネントはグローバルに登録されるという点だけ、念の為心に留めておいたほうがよさそうです。
自分自身をimportしてcomponentsに登録する
自分で自分を再帰的に呼び出します。
MyComment
コンポーネントの propscomment
にはreply
を渡す
<li v-for="reply in comment.replies">
v-forで回しているreply
には、上のcomment
オブジェクトと同じ形のデータが入っているので、これを渡してあげます。
入れ子のようなスタイル
li
要素の上、左にマージンをとることで、上の要素との余白を確保しつつ、
入れ子になった際にどんどん右にインデントされていくスタイルを実現できます。
<style>
@charset 'utf8';
.reply {
display: block;
margin-left: 2rem;
margin-top: 1rem;
}
</style>
感想
仕事で、画像のようなコメントのUIを作るときに
name
オプションを使ってコンポーネントを再帰的に呼び出せることを知ったので、
備忘録として残しておこうと思いました。
もし、この記事が、同じようなことをしようとしている方のお役に立てたなら幸いです。