(6) 子要素を持つコンポーネント - デザイナー向けのVue.js紹介
「HTML/CSSはわかるけれど、JavaScriptは少し読んだことがあるくらい」というデザイナー向けの、Vue.js紹介記事です。
この記事では、子要素を持つコンポーネントを作る方法を解説します。
連載をまとめたマガジンはこちら↓
コンポーネントのルート要素
前回までに作ったコンポーネントは、単一のHTML要素を持つコンポーネントでした。今回からは、もっと複雑なコンポーネントを作っていきましょう。
複雑なコンポーネントを作るにあたり、覚えておくべき制約があります。Vueコンポーネントでは、ルートの要素は1つでなければいけません。
正しい例
<template>
<hr> <!-- ⭕ルートに要素が1つ -->
</template>
正しくない例
<template>
<br>
<hr> <!-- ❌ルートに要素が2つある! -->
</template>
2つ以上の要素を並べたい場合は、<div>や<span>などの要素でラップすることになります。
<template>
<div> <!-- ⭕ルートに要素が1つ -->
<br>
<hr> <!-- ⭕ルートでなければ兄弟要素もOK -->
</div>
</template>
コンポーネントの追加
今回は例題として、よくある「ボタン型リンク」を作ってみましょう。
使用するのは<a>タグで、CSSを使ってボタンの形にします。中には基本的にテキスト、たまにアイコンも表示すると想定します。
まずは第4回でやったように、新しいコンポーネントの名前を考え、ファイルを作成します。今回はBaseButtonコンポーネントと名付けてみます。
componentsフォルダにBaseButton.vueを作成し、BaseLineコンポーネントから中身をコピーしてきて、必要部分だけ書き換えましょう。
<template>
<a>ボタン</a>
</template>
<script>
export default {
name: "BaseButton"
};
</script>
<style>
</style>
コンポーネントとして動作する最低限の形ができたら、実際に表示しながらスタイルを修正できるよう、App.vueにBaseButtonコンポーネントを表示してみましょう。
(第4回までの復習です。上手くいかない場合はこのサンドボックスと見比べてください。)
表示できたら、プレビューを見ながら<a>にCSSを記述して、ボタンの形にしていきます。
今回は以下のようなCSSを適用しました。第5回と同様にscopedかmoduleを使ってスタイルを閉じ込め、共通CSSに色を抜き出してみましょう。
:root {
--fg-border: rgb(169, 173, 177);
--bg-button-hover: rgb(207, 215, 221);
}
.button {
display: inline-flex;
flex-direction: row;
align-items: center;
box-sizing: border-box;
padding: 0 1em;
height: 2.5em;
border-radius: 0.25em;
border: none;
cursor: pointer;
user-select: none;
text-decoration: none;
color: var(--fg-color);
background-color: transparent;
border: 1px solid var(--fg-border);
opacity: 0.8;
}
.button:hover,
.button:active,
.button:visited {
color: var(--fg-color);
}
.button:hover,
.button:active {
background-color: var(--bg-button-hover);
opacity: 1;
}
リンクの子要素
実際にBaseButtonコンポーネントを使用する際は、標準の<a>や<button>タグと同じようにコンポーネントを使う側でボタンの中身を指定するのが理想的です。以下の画像のようなイメージです。
そのために、Vueにはスロットという機能が用意されています。
slot(スロット)コンポーネント
Vueのtemplate内では、Vueの機能として用意された特別なコンポーネントを使うことができます。<slot>もそのひとつです。
<slot>は「コンポーネントに渡された子要素がここに入る」という機能を持っています。言葉で説明するより、実際にやってみましょう。
BaseButton.vueの<template>内で、<a>タグの中に直接文字を書いていた部分を<slot />に変更します。
<template>
<a :class="$style.button">
<slot/>
</a>
</template>
次に、App.vueの<template>内で<BaseButton>タグに文字列を渡します。
<BaseButton>ボタンのテキスト</BaseButton>
これだけで、BaseButtonを使う側から子要素を渡し、自由に中身を変更できるようになります。(slotを適用したサンドボックスはこちら)
今回のまとめ
・コンポーネントの<template>内では、ルート要素は1つでないといけない
・<template>内ではVueの機能として用意された特別なコンポーネントが使える
・<slot>コンポーネントを使うとコンポーネントに渡された子要素をそこに入れることができる
-----------------------------
ここまでお読みいただきありがとうございました。次回は、コンポーネントに属性値を渡す方法を解説します。
-----------------------------
おまけ:練習課題
・BaseButtonコンポーネントに渡す文字列を変更して、中身が変更されることを確認しましょう。
・<slot>には文字列だけでなく、他のHTML要素やコンポーネントを渡すこともできます。ボタンの中に画像を表示してみましょう。
・<slot>には「子要素が空だった場合のデフォルト」を設定する機能があります。BaseButtonコンポーネントの<template>を以下のように書き換えてみましょう。
<template>
<a :class="$style.button">
<slot>
(slotタグに中身があると、デフォルトの子要素として使用されます)
</slot>
</a>
</template>
・BaseButtonコンポーネントにリンクボタンとして適切なデフォルト内容を設定し、動作するか確認してみましょう。
🐥はげみになります!