tohokuaikiのチラシの裏

技術的ネタとか。

Vueのslotの感覚が良くわからないから図にしてみた

slot…Vue2をやってた時に、なんかわかりにくいなぁ…とずっと思ってました。

で、Reactやってから見てみたら「あ、これComponentのchildrenなんだわ」ってわかってスゲー腑に落ちた。

ただ、Vueの場合はそのchildrenの要素に名前を付けられるのが便利といえば便利かな…?

ということで、いろいろと試して「あ、こういうこと」ってわかったのでまとめる。slotってその場所にカードを差し込む差込口みたいなイメージなんだけど、名称としてはReactみたいにComponentのchildrenの方がすっとくるかな。Reactの場合は、Propsに付属しているものだしそれがまた「Componentに渡すもの」ってイメージがついて直良し。

slotの感覚をつかむために考えたこと

slotは「親」とか「子」の感覚で見ない方がいい

「親」とか「子」ってわかりやすいようでわかりにくい。たとえば、この場合

<template>
  <Child>
    <div>このDIV要素は親?子?</div>
  </Child>
</template>
<template>
    <p>このP要素は親?子?</p>
</template>

page.vueのDIV要素は、Childコンポーネントから見ると「子」なわけですよ。

だけど、Childコンポーネントは、page.vueの「子」であって、DIV要素はChildコンポーネントの「親」であるpage.vueにあるから

あれ?DIV要素ってChildコンポーネントの親なんじゃね?

とかいう感覚に囚われていた。てかさー、ChildコンポーネントってDIV要素からすると親なのに「Child」っておかしくない?

っつーことで、「親」とか「子」っていうの禁止。

単純にslotってコンポーネントで囲った中身なんすよ。

それだけだった…。

名前付きslotって便利(?)なのだけど、それがますますわかりにくくしてる気がする。

コンポーネントの中にあれば、どこに書いてもいいし。ある意味HTML構造を無視しちゃってるから不穏な感じがするんだよね。名前付きslot

NuxtのLayoutで使うslotはまた特殊な感じがする

NuxtのLayoutのslotはComponentの中身を移譲するという意味では同じなんだけど、ラップしている(HTML構造でいえば親になる)部分にslotを使っているのがとても違和感を感じさせる。

サンプル

図解するとこんな感じ。

<template>
  <ul class="global-menu">
    <slot name="menu">
      <li>初期メニュー</li>
    </slot>
    <slot />
  </ul>
</template>
<script setup>
definePageMeta({
    layout: false
})
import Banner from "../components/Banner.vue"
import Subcontent from "../components/Subcontent.vue"
</script>
<template>
    <NuxtLayout name="custom">
        <template #menu>
            <li>メニュー1</li>
            <li>メニュー2</li>
        </template>
        <div>
            <p>メインとなるコンテンツ</p>
            <Subcontent>
                <h3>フルーツ</h3>
                <ol>
                    <li>バナナ</li>
                    <li>リンゴ</li>
                    <li>メロン</li>
                </ol>
            </Subcontent>
        </div>
        <Banner>
            <img src="~/assets/img/banner.png" />
            <template #caption>赤バナー</template>
        </Banner>
    </NuxtLayout>
</template>
<style>
img {
    width: 120px;
}
</style>
<template>
    <h2>サブコンテンツ:一覧</h2>
    <slot />
</template>
<template>
    <slot><img src="~/assets/img/banner_default.png"/></slot>
    <div>
        Caption:<slot name="caption"/>
    </div>
</template>

出力されてるHTML