本記事は Vue.js コンポーネント入門の第6回です。
slot という機能を紹介します。
前提
PC | ブラウザ | Node | npm | Vue |
---|---|---|---|---|
macOS 10.12 | Firefox Quantum | 9.3.0 | 5.5.1 | 2.5 |
コンテンツの差し込み
差し込みと言っているのは、マニュアルでは「スロットによるコンテンツ配信」という言葉を使っていますが、つまりタグの間にテキストノードもしくは他の HTML 要素、Vue コンポーネントを挟み込むことです。
既存の HTML 要素で見ると分かりやすいでしょう。
<!-- テキストノードの差し込み -->
<a href="https://qiita.com">Qiitaのページ<a>
<!-- 他要素の差し込み -->
<dl>
<dt>HTML</dt>
<dd>HTMLは、ハイパーテキストを記述するためのマークアップ言語の1つである。</dd>
</dl>
最初の例では <a>
要素にテキストノードを差し込んでいます。
ふたつめの例では <dl>
要素に <dt>
および <dd>
要素を差し込んでいます。
Vue コンポーネントでも同じことができるというわけです。
単一コンテンツの差し込み
第3回で扱った AnchorLink
コンポーネントを見返してください。
<anchor-link href="https://qiita.com" text="Qiitaのページ" :new-tab="true"></anchor-link>
<template>
<span class="anchor" @click="onClickLink">{{ text }}</span>
</template>
第3回めの時点では props しか紹介していませんでしたので、表示する文字列を props の text
として渡しています。しかし、以下の記述の方が自然なのではないでしょうか。
<anchor-link href="https://qiita.com" :new-tab="true">Qiitaのページ</anchor-link>
上記の記述は可能です。そしてコンポーネント側で差し込まれたコンテンツを受け取るのが <slot>
です。
<template>
<span class="anchor" @click="onClickLink">
<slot></slot>
</span>
</template>
<slot></slot>
の部分が差し込まれたコンテンツに置きかわります。
コードの全体を貼っておきますので確認してみてください。
複数コンテンツの差し込み
コンテンツは複数差し込むこともできます。<dl>
の例のような感じです。
複数差し込む場合は、差し込むコンテンツに slot
属性を持たせます。
<my-article>
<span slot="title">スロットによるコンテンツ配信</span>
<div slot="content">
<p>HTML 要素と同様に、コンポーネントにコンテンツを渡すことが...</p>
</div>
</my-article>
コンポーネント側では、<slot>
の name
属性で差し込むべき要素を特定します。
<template>
<article>
<h1>
<slot name="title"></slot>
</h1>
<slot name="content"></slot>
</article>
</template>
サンプルコンポーネント
サンプルとしてカードコンポーネントを作ってみましょう。
こちらが完成形です。
Getting started
# プロジェクトディレクトリで以下のコマンドを実行してください。
$ git clone git@github.com:MasahiroHarada/vue-components-starter-kit.git chapter-6
$ cd chapter-6
$ git checkout scss
$ npm install
$ npm install --save-dev spectre.css
コンポーネント
コンポーネントから作成していきます。
前回に引き続き今回も Spectre.css を使用します。
<template>
<div class="card">
<div class="card-image">
<slot name="image"></slot>
</div>
<div class="card-header">
<div class="card-title h5">
<slot name="title"></slot>
</div>
<div class="card-subtitle text-gray">
<slot name="subtitle"></slot>
</div>
</div>
<div class="card-body">
<slot name="body"></slot>
</div>
<div class="card-footer">
<slot name="button"></slot>
</div>
</div>
</template>
画像、タイトル、サブタイトル(灰色の文字)、文章、ボタンを差し込めるように作っています。
スタイルシート
続いて SCSS ファイルです。
$primary-color: #0052cc;
@import "../node_modules/spectre.css/src/spectre.scss";
#app {
padding: 1rem;
}
.card-body {
p {
margin: 0;
}
}
.card-image {
img {
width: 100%;
}
}
エントリポイント
コンポーネントを登録します。
onClick
メソッドが Card
コンポーネント側ではなく Card
コンポーネントを使う側(今回の場合はルートコンポーネント)に定義されている点に注意してください。
import Vue from "vue"
// Style
import "./app.scss"
// Components
import Card from "./components/Card.vue"
const app = new Vue({
el: "#app",
components: {
Card
},
methods: {
onClick () {
alert("Clicked !")
}
}
})
HTML
最後に HTML ファイルです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Vue Component Tutorial</title>
<link rel="stylesheet" href="./dist/app.css">
</head>
<body>
<div id="app">
<div class="container">
<div class="columns">
<div class="column col-6 col-md-8 col-mx-auto">
<card>
<img slot="image" src="https://source.unsplash.com/g1Kr4Ozfoac/1600x900">
<span slot="title">Microsoft</span>
<span slot="subtitle">Software and hardware</span>
<p slot="body">Empower every person and every organization on the planet to achieve more.</p>
<button slot="button" class="btn btn-primary" @click="onClick">Click me</button>
</card>
</div>
</div>
</div>
</div>
<script src="./dist/main.js"></script>
</body>
</html>
先ほども少し触れましたが、onClick
メソッドはカードコンポーネントを使う側に定義されていなければいけません。差し込むからと言って差し込む先のコンポーネントに定義するわけではないのですね。あくまでその変数なり関数が見えている場所に定義されていなければならないというルールには気をつけてください。
ちょっと余談ですが、このようなコンポーネントの何が嬉しいかというと、カードを表現するためのマークアップ上のルールを隠ぺいできることです。
今回は Spectre.css を使いましたので、カードを表現するためにはまず全体が card
クラスで覆われていないといけませんし、画像は card-image
クラスで囲まれていなければいけません。
このようなルールは他の CSS フレームワークでも、フレームワークを使っていなくても発生します。このルールをコンポーネントに閉じ込めてしまうことで、カード表現を使うたびにいちいちルールを意識する必要はなくなりますし、ルールそのものを変更したい(フレームワークから自前のスタイルに変更など)場合も、うまくいけば変更箇所はコンポーネントだけで済むかもしれません。
以上、本記事では Vue.js コンポーネント入門の第6回として、slotによるコンテンツの差し込み機能を紹介しました。
関連記事
Vue.js コンポーネント入門(全7回)
- Vue.jsコンポーネント入門 (1) 環境設定
- Vue.jsコンポーネント入門 (2) コンポーネントとは何か?
- Vue.jsコンポーネント入門 (3) propsによるデータの受け渡し
- Vue.jsコンポーネント入門 (4) $emitによるイベントの発行
- Vue.jsコンポーネント入門 (5) コンポーネント間のコミュニケーション
- Vue.jsコンポーネント入門 (6) slotによるコンテンツの差し込み
- Vue.jsコンポーネント入門 (7) ライフサイクルフック