Vue.jsで再利用可能なタブのUIコンポーネントを実装する方法を解説
Post on:2021年10月12日
タブは、Webサイトやスマホアプリでよく使用されるUIコンポーネントです。TailwindCSSやBootstrapなどのライブラリにも必ずありますが、再利用可能な柔軟性のあるものではありません。
スタイルを簡単にカスタマイズできる再利用可能なタブコンポーネントをVue.jsで実装する方法を紹介します。
How to Build a Tab Component In Vue.js
by Luca Spezzano
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
はじめに
タブはUIでよく使用されるコンポーネントの1つで、Bootstrapのような人気のあるCSSフレームワークやTailwindUIのようなUIコンポーネントのライブラリにも必ずあります。
タブにはさまざまなスタイルがありますが、主な役割はページにとどまりながらタブをクリックするだけでコンテンツを変更することです。
タブのコンポーネント
私はプロジェクトでBootstrapのタブを使用していましたが、Vue.jsとTailwindCSSを使い始めてからは、タブのコンポーネントを一から開発する方法を考えなければなりませんでした。
Vue.jsを使用してタブを実装するのは非常に簡単ですが、カスタマイズができる柔軟性を持った再利用可能なコンポーネントを実装するのは複雑です。
この記事では、再利用可能なタブのコンポーネントを実装する方法を段階的に解説します。
実装の概要
私は、2つのデモを用意しました。
1つはこの記事で解説するスタイルなしのデモと、もう1つはCodesandboxにアップロードしたTailwind CSSを使用してより美しくしたデモです。
この記事ではCSSスタイルはなしで、機能にフォーカスしています。(タブを水平・垂直に配置するためのCSSのクラスが1つだけあります)。
CSSのクラスが追加されたバージョンを確認したい場合は、デモページをご覧ください。
AppTabs.vueコンポーネントの作成
まずは、コアコンポーネントとなるAppTabs.vueを作成します。
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<template> <div :class="{ flex: variant === 'horizontal', }" > <ul :class="{ flex: variant === 'vertical', }" > <li v-for="(tab, index) in tabList" :key="index"> <label :for="`${_uid}${index}`" v-text="tab" /> <input :id="`${_uid}${index}`" type="radio" :name="`${_uid}-tab`" :value="index + 1" v-model="activeTab" /> </li> </ul> <template v-for="(tab, index) in tabList"> <div :key="index" v-if="index + 1 === activeTab"> <slot :name="`tabPanel-${index + 1}`" /> </div> </template> </div> </template> <script> export default { props: { tabList: { type: Array, required: true, }, variant: { type: String, required: false, default: () => "vertical", validator: (value) => ["horizontal", "vertical"].includes(value), }, }, data() { return { activeTab: 1, }; }, }; </script> <style> .flex { display: flex; } </style> |
コードの解説
上記のコードを解説します。
Propsとnamed slots
このコンポーネントでは、親がコンテンツを選択することができます。タブ(ナビゲーションリンク)はprop tabList経由で、コンテンツはnamed slots経由で指定します。
named slotsは、indexのおかげでtabList内に存在する要素の数に基づいて、動的な名前で生成されます。
named slotsを自動生成するという解決策にたどり着いたのは、同僚のLuca Stefano Sartoriとの会話がきっかけでした。
named slots(名前付きSlots)の詳細については、下記をご覧ください
The Difference Between Props, Slots and Scoped Slots in Vue.js
翻訳版: Vue.jsでSlotsの代わりにPropsを使用する理由、名前付きSlotsやスコープ付きSlotsとの違いについて解説
唯一の要件はtabListの数がnamed slotsと同じであることです。
ご覧のように、variantというpropもあり、これはコンポーネントが受け取ったpropに基づいて1つのCSSクラスを動的に適用することで、コンテンツの配置を変更することができます。デフォルトでは、このpropはverticalです。
v-model
最初は、radio(ラジオボタン)の代わりにbuttonを使おうと思っていました。確かにその方が適切でしたが、変数に値を代入するためのコードをさらに書くことになってしまいました。その後、同僚と再び話しているうちに、この動作はv-modelを使用して自動的に再現できることがわかりました。
ユニークなid
なぜこのコンポーネントで_uid変数が使用されているのかというと、理由は簡単で、HTMLタグに固有の属性を持たせることができるからです。これにより、同じページでこのコンポーネントを何度も使用しても問題はありません。
コンポーネントの使用方法
このコンポーネントの使用方法を見てみましょう。
App.vueというページ内で使用します。
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 |
<template> <div> <app-tabs :tabList="tabList"> <template v-slot:tabPanel-1> Content 1 </template> <template v-slot:tabPanel-2> Content 2 </template> <template v-slot:tabPanel-3> Content 3 </template> <template v-slot:tabPanel-4> Content 4 </template> </app-tabs> </div> </template> <script> import AppTabs from "./components/AppTabs"; export default { components: { AppTabs, }, data() { return { tabList: ["Tab 1", "Tab 2", "Tab 3", "Tab 4"], }; }, }; </script> |
このコンポーネントは非常に簡単に使用できます。
必要なのは、このコンポーネントに、
- タブの配列: tabList
- named slotsを持つさまざまなタブのコンテンツ: tabPanel-${index}(indexはtabList配列のindex + 1に相当します。
水平レイアウトを使用したい場合は、下記のようにコンポーネントにvariantを追加します。
1 |
<app-tabs :tabList=”tabList” variant=”horizontal”> |
終わりに
タブを実装するためのUIライブラリは数多くあります。しかし、これらのライブラリは必要でしょうか?
多くのライブラリは柔軟性に欠け、アプリのサイズを大きくしてしまいます。
モーダル、タブ、ドロップダウンなどのコンポーネントをゼロから実装することは、プロジェクトに最適なソリューションになることが多々あります。
実際の動作は、下記でご覧ください。
sponsors