LoginSignup
1
0

More than 1 year has passed since last update.

Vue2 コンポーネントのラッパーコンポーネントの作り方

Last updated at Posted at 2022-04-26

この記事でわかること

  • Vue2 のスロットの使い方
  • Vue2 コンポーネントのラッパーコンポーネントの作り方

結論

ラッパーコンポーネントの作り方

VueComponentWrapper.vue
<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>

または、デフォルトスロット名を省略して

VueComponentWrapper.vue
<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>

または、デフォルトスロットのテンプレートを省略して

VueComponentWrapper.vue
<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>

特定のスロットのデフォルト値をバインドする

VueComponentWrapperWithBindingSlotDefault.vue
<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>

スロットに静的なコンテンツを付加する

VueComponentWrapperWithStaticContent.vue
<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 要素を流し込む機能がある。例えば、以下のようなコンポーネントがある場合、

MyComponent.vue
<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>

このコンポーネントの外部から、以下のような実装でコードを流し込むことができる。

Main.vue
<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 になる。

main.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 をバインドしたい。つまり、

Main.vue
<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>

これをコンパイルして以下を得られるようにしたい。

main.html
<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>

この場合、ラッパーコンポーネントの最も単純な実装は以下のようになる。

MyComponentWrapper.vue
<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,
}

これを利用して、ラッパーコンポーネントの実装を以下のように改善できる。

MyComponentWrapper2.vue
<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>

ラッパーコンポーネントを作りスロットに静的なコンテンツを付加する

以下のような実装で、ラッパーコンポーネントのスロットの内容に静的なコンテンツを付与することができる。

MyComponentWrapperWithStaticContent.vue
<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>
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0