こんな要望にお答えします。
僕は最近よく仕事でVue.jsを触っていますので、信頼いただける情報かなと思います。
この記事は以下の環境で動作確認しました。
1 2 3 4 5 6 7 8 | $ node -v v8.11.4 $ npm -v 6.4.1 $ vue -V 3.0.5 |
Vueプロジェクトの開発環境が整っていない方は、「Vue CLIを使った開発環境構築方法」で環境構築後に読み進めてください。
slotについて
Vue.jsのコンポーネントでは、基本的に開始タグと終了タグの間のコンテンツは無視されてレンダリングされます。以下のMyComponent要素の間に書かれた「こんにちは」という文字は無視されます。
<MyComponent>こんにちは</MyComponent>
つぎの例をご確認ください。
/src/components/MyButton.vueを作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <template> <button>OK</button> </template> <style lang="sass" scoped> button background-color: #333; border: none; color: #fff; padding: .5em 1em; border-radius: .3em; cursor: pointer; margin: 0 .3em; font-size: 16px; &:hover opacity: .8; </style> |
/src/views/About.vueをつぎのとおり編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <template> <div class="about"> <h1>This is an about page</h1> <MyButton></MyButton> </div> </template> <script> import MyButton from '@/components/MyButton.vue'; export default { components: { MyButton, }, }; </script> |
Aboutページの表示はつぎのようになります。

Vue.jsのコンポーネントでは、基本的に開始タグと終了タグの間のコンテンツは無視されてレンダリングされることを証明するために、/src/views/About.vueをつぎのとおり編集します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <template> <div class="about"> <h1>This is an about page</h1> <MyButton>Cancel</MyButton> <!-- Cancelという文字列を追加 --> </div> </template> <script> import MyButton from '@/components/MyButton.vue'; export default { components: { MyButton, }, }; </script> |
ボタンのOKという文字をCancelに変えることが狙いだったのですが適用されませんでした。開始タグと終了タグの間のコンテンツは無視されることが確認できました。
では、ボタンのOKをCancelという文字に変更するにはどうしたらいいでしょうか。
ここで、slotが登場します。
/src/components/MyButton.vueをつぎのとおり編集してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <template> <button><slot>OK</slot></button> <!-- OKという文字をslot要素で囲みます --> </template> <style lang="sass" scoped> button background-color: #333; border: none; color: #fff; padding: .5em 1em; border-radius: .3em; cursor: pointer; margin: 0 .3em; font-size: 16px; &:hover opacity: .8; </style> |
今度はボタンの文字がCancelに変わりました。

このように、コンポーネント外からコンテンツを受け付ける仕組みがslotです。今回の例のボタンのように外からコンテンツを受け付けたほうが再利用性が高まる場合は、slotの利用をおすすめします。
名前付きslotについて
slotに名前を付けることでコンポーネント内で複数のslotを使うことができます。実際に使ってみましょう。
/src/components/Book.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 | <template> <div class="book"> <dl> <dt><slot name="title">Title</slot></dt> <dd><slot name="summary">Summary</slot></dd> </dl> </div> </template> <style lang="sass" scoped> .book max-width: 600px margin: 0 auto padding: 1.5em .5em border-bottom: 1px solid #ddd box-sizing: border-box dl margin: 0 dt font-weight: bold margin-bottom: .5em font-size: 1.2em p margin: 0 dd margin-left: 0 text-align: left line-height: 1.8 p margin: 0 </style> |
titleという名前のslotとsummaryという名前のslotを設定しています。
/src/views/Home.vueをつぎのように編集して名前付きslotを使ってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <template> <div class="home"> <Book> <template slot="title">坊ちゃん / 夏目漱石</template> <template slot="summary">親譲おやゆずりの無鉄砲むてっぽうで小供の時から損ばかりしている。小学校に居る時分学校の二階から飛び降りて一週間ほど腰こしを抜ぬかした事がある。</template> </Book> <Book> <p slot="title">銀河鉄道の夜 / 宮沢賢治</p> <p slot="summary">「ではみなさんは、そういうふうに川だと云いわれたり、乳の流れたあとだと云われたりしていたこのぼんやりと白いものがほんとうは何かご承知ですか。」</p> </Book> </div> </template> <script> import Book from '@/components/Book.vue'; export default { name: 'home', components: { Book, }, }; </script> |
つぎのような画面が表示されましたか?

名前付きslotはtemplate要素に「slot=”title”」などと指定することで使用することができます。
または、「<p slot=”title”>銀河鉄道の夜 / 宮沢賢治</p>」のように要素に直接指定する方法もあります。
slotの注意点
slotを使用する際の注意点があります。
スロット内でデータバインディングを使った場合は、コンポーネントを使ったテンプレート内のデータがバインディングされます。
試しに、先ほど使った/src/components/MyButton.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 | <template> <button><slot>OK</slot></button> </template> <style lang="sass" scoped> button background-color: #333; border: none; color: #fff; padding: .5em 1em; border-radius: .3em; cursor: pointer; margin: 0 .3em; font-size: 16px; &:hover opacity: .8; </style> <script> <!-- scriptブロックを追加します --> export default { data: () => { return { <strong>button: 'Stop',</strong> }; }, } </script> |
scriptブロック内でdataプロパティを定義しています。dataプロパティにはbuttonというキーを定義しています。buttonの値はStopという文字列です。
つぎに、/src/views/About.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 | <template> <div class="about"> <h1>This is an about page</h1> <MyButton>Cancel</MyButton> <MyButton>{{ button }}</MyButton> <!-- 追加 --> </div> </template> <script> import MyButton from '@/components/MyButton.vue'; export default { // ここから追加 data: () => { return { button: 'Start', }; }, // ここまで追加 components: { MyButton, }, }; </script> |
templateブロックとscriptブロックに追加します。こちらのscriptブロックのdataプロパティにも先ほどと同名のbuttonというキーを登録しています。値はStartという文字列です。
さあ、{{ button }}にはStartとStopどちらの文字が表示されるでしょうか?

Startが表示されました。最初に書いたとおり、スロット内でデータバインディングを使った場合は、コンポーネントを使ったテンプレート内のデータがバインディングされることが証明されました。