この記事でわかること
- Vue2 のスロットの使い方
- Vue2 コンポーネントのラッパーコンポーネントの作り方
結論
ラッパーコンポーネントの作り方
<template>
<vue-component
v-bind="$attrs"
v-on="$listeners"
>
<template v-slot:default>
<slot name="default"></slot>
</template>
<template
v-for="(_, name) in $scopedSlots"
v-slot:[name]="slotProps"
>
<slot v-bind:name="name" v-bind="slotProps"></slot>
</template>
</vue-component>
</template>
または、デフォルトスロット名を省略して
<template>
<vue-component
v-bind="$attrs"
v-on="$listeners"
>
<template v-slot>
<slot></slot>
</template>
<template
v-for="(_, name) in $scopedSlots"
v-slot:[name]="slotProps"
>
<slot v-bind:name="name" v-bind="slotProps"></slot>
</template>
</vue-component>
</template>
または、デフォルトスロットのテンプレートを省略して
<template>
<vue-component
v-bind="$attrs"
v-on="$listeners"
>
<slot></slot>
<template
v-for="(_, name) in $scopedSlots"
v-slot:[name]="slotProps"
>
<slot v-bind:name="name" v-bind="slotProps"></slot>
</template>
</vue-component>
</template>
特定のスロットのデフォルト値をバインドする
<template>
<vue-component
v-bind="$attrs"
v-on="$listeners"
>
<slot></slot>
<template v-slot:some-binded-slot>
<slot name="some-binded-slot">BINDED_DEFAULT</slot>
</template>
<template
v-for="(_, name) in $scopedSlots"
v-slot:[name]="slotProps"
>
<slot v-bind:name="name" v-bind="slotProps"></slot>
</template>
</vue-component>
</template>
スロットに静的なコンテンツを付加する
<template>
<vue-component
v-bind="$attrs"
v-on="$listeners"
>
STATIC_CONTENT_FOR_DEFAULT_SLOT
<slot></slot>
<template
v-for="(_, name) in $scopedSlots"
v-slot:[name]="slotProps"
>
STATIC_CONTENT_FOR_NAMED_SLOT
<slot v-bind:name="name" v-bind="slotProps"></slot>
</template>
</vue-component>
</template>
詳細
Vue のスロットの使い方
Vue には、あるコンポーネントの持つスロットに対して、外部からコンポーネントや DOM 要素を流し込む機能がある。例えば、以下のようなコンポーネントがある場合、
<template>
<div
v-bind="$attrs"
v-on="$listeners"
>
DEFAULT:<slot>MY_COMPONENT_DEFAULT</slot>
NAMED:<slot name="named">MY_COMPONENT_DEFAULT</slot>
NAMED_WITH_ARGS:<slot name="named-with-args" x="1">MY_COMPONENT_DEFAULT</slot>
</div>
</template>
このコンポーネントの外部から、以下のような実装でコードを流し込むことができる。
<template>
<my-component>
</my-component>
<my-component>
<h1>MAIN</h1>
<template v-slot:named>MAIN</template>
<template v-slot:named-with-args="{ x }">MAIN({{ x }})</template>
<h2>MAIN</h2>
</my-component>
</template>
これをコンパイルすると、以下のような HTML になる。
<div>
DEFAULT:MY_COMPONENT_DEFAULT
NAMED:MY_COMPONENT_DEFAULT
NAMED_WITH_ARGS:MY_COMPONENT_DEFAULT
</div>
<div>
DEFAULT:<h1>MAIN</h1>
<h2>MAIN</h2>
NAMED:MAIN
NAMED_WITH_ARGS:MAIN(1)
</div>
ラッパーコンポーネントを作りスロットのデフォルト値をバインドする
このコンポーネントのラッパーコンポーネント MyComponentWrapper.vue
を作り、named
スロットのデフォルト値に MY_COMPONENT_WRAPPER_DEFAULT
をバインドしたい。つまり、
<template>
<my-component>
</my-component>
<my-component-wrapper>
</my-component-wrapper>
<my-component-wrapper>
<h1>MAIN</h1>
<template v-slot:named>MAIN</template>
<template v-slot:named-with-args="{ x }">MAIN({{ x }})</template>
<h2>MAIN</h2>
</my-component-wrapper>
</template>
これをコンパイルして以下を得られるようにしたい。
<div>
DEFAULT:MY_COMPONENT_DEFAULT
NAMED:MY_COMPONENT_DEFAULT
NAMED_WITH_ARGS:MY_COMPONENT_DEFAULT
</div>
<div>
DEFAULT:MY_COMPONENT_DEFAULT
NAMED:[[MY_COMPONENT_WRAPPER_DEFAULT]]
NAMED_WITH_ARGS:MY_COMPONENT_DEFAULT
</div>
<div>
DEFAULT:<h1>MAIN</h1>
<h2>MAIN</h2>
NAMED:MAIN
NAMED_WITH_ARGS:MAIN(1)
</div>
この場合、ラッパーコンポーネントの最も単純な実装は以下のようになる。
<template>
<my-component
v-bind="$attrs"
v-on="$listeners"
>
<slot></slot>
<template v-slot:named>
<slot name="named">[[MY_COMPONENT_WRAPPER_DEFAULT]]</slot>
</template>
<template v-slot:named-with-args="args">
<slot name="named-with-args" v-bind="args"></slot>
</template>
</my-component>
</template>
しかし、この実装ではラップするコンポーネントのスロットの仕様が変更される度にラッパーコンポーネントの実装もそれに追従する必要があり、変更に弱い。そこで、$scopedSlots
というコンポーネントインスタンスプロパティを使う。例えば、あるコンポーネントに対して以下のようなスロットテンプレートが記述されている場合、
<template>
<some-component>
<template v-slot:named></template>
<template v-slot:named-with-args></template>
</some-component>
</template>
コンポーネントインスタンスの $scopedSlots
は以下のようになる。
{
'named': renderFunction,
'named-with-args': renderFunction,
}
これを利用して、ラッパーコンポーネントの実装を以下のように改善できる。
<template>
<my-component
v-bind="$attrs"
v-on="$listeners"
>
<slot></slot>
<template v-slot:named>
<slot name="named">[[MY_COMPONENT_WRAPPER_DEFAULT]]</slot>
</template>
<template
v-for="(_, name) in $scopedSlots"
v-slot:[name]="slotProps"
>
<slot v-bind:name="name" v-bind="slotProps"></slot>
</template>
</my-component>
</template>
ラッパーコンポーネントを作りスロットに静的なコンテンツを付加する
以下のような実装で、ラッパーコンポーネントのスロットの内容に静的なコンテンツを付与することができる。
<template>
<my-component
v-bind="$attrs"
v-on="$listeners"
>
MY_COMPONENT_WRAPPER_STATIC_CONTENT_FOR_DEFAULT_SLOT
<!-- デフォルトスロットに静的なコンテンツを付加 -->
<slot></slot>
<template v-slot:named>
MY_COMPONENT_WRAPPER_STATIC_CONTENT_FOR_SPECIFIC_SLOT
<!-- named という名前のスロットに静的なコンテンツを付加 -->
<slot name="named">[[MY_COMPONENT_WRAPPER_DEFAULT]]</slot>
</template>
<template
v-for="(_, name) in $scopedSlots"
v-slot:[name]="slotProps"
>
MY_COMPONENT_WRAPPER_STATIC_CONTENT_FOR_NAMED_SLOT
<!-- 名前付きスロットに静的なコンテンツを付加 -->
<slot v-bind:name="name" v-bind="slotProps"></slot>
</template>
</my-component>
</template>