見出し画像

(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を使ってボタンの形にします。中には基本的にテキスト、たまにアイコンも表示すると想定します。

画像3

まずは第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回と同様にscopedmoduleを使ってスタイルを閉じ込め共通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;
}

画像2

スタイルを適用したサンドボックスはこちら

リンクの子要素

実際にBaseButtonコンポーネントを使用する際は、標準の<a>や<button>タグと同じようにコンポーネントを使う側でボタンの中身を指定するのが理想的です。以下の画像のようなイメージです。

画像3

そのために、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コンポーネントにリンクボタンとして適切なデフォルト内容を設定し、動作するか確認してみましょう。

🐥はげみになります!