添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
109
85

More than 5 years have passed since last update.

Vue.jsにおけるRender PropとScoped Slotsについて

Posted at

こんにちは。転職によりReact畑からVue畑に乗り換えることになったフロントエンドエンジニアです。

Vueでも描画関数使ってればRender Propsも使えるじゃんと思ったところ、Vue.js 作者のEvan You曰く「Render PropパターンはVue.jsにおけるScoped Slotsと同じ」とのこと。

In case you are wondering what’s the equivalent pattern in Vue, it’s called scoped slots (and if using JSX it works the same as React)

— Evan You (@youyuxi) 2017年9月25日

本当にそうなのか簡単な実装例を用意してみました。

Render Prop

まず、そもそものRender Propを使う目的としては
「コードの再利用性を高めるための実装パターンで、複数のコンポーネントに適用したい汎用的な振る舞いを抽出すること」であると捉えています。

少し前に Render Prop使えばHOCいらんくね?という記事 が話題になりましたが、HOCみたいにあるコンポーネントに特定の振る舞いを追加するのが目的と考えるとイメージしやすいと思います。

パターンの詳細についてですが、実装を見るのが一番早いと思うので、下記にVue.jsでの実装例を紹介したいと思います。
今回は例として、ありがちな「選択された要素だけスタイルを変える」という振る舞いを実装してみます。

App.vue
//「選択された要素だけスタイルを変える」振る舞いを持つコンポーネントです。
//このコンポーネントでは `render` propに渡された関数を実行することによって描画します。
const Selected = {
  props: {
    render: {
      default: h => null
  data() {
    return {
      selectedVal: 0
  methods: {
    select(value) {
      this.selectedVal = value;
  render() {
    return this.$props.render({
      selectedVal: this.selectedVal,
      select: this.select
export default {
  functional: true,
  render: (h, { props }) => (
      <Selected
        render={({ selectedVal, select }) => (
            <input
              type="number"
              onChange={event => select(event.target.value)}
              value={selectedVal}
              {props.items.map((item, i) => (
                /* ここで選択されたものだけスタイルを変えています */
                <li class={selectedVal == i ? "selected-item" : ""} onClick={event => select(i)}> {{ item }} </li>

初見だと分かりづらいかもな、と思うのがこの Selected コンポーネントは自身のViewを持たないということです。render関数内でpropsとして渡されたrenderメソッドを呼び出しているだけで、自身はロジックしか持っていません。もはやこれを"コンポーネント"と呼ぶのが正しいのかすら微妙ですが、パターンとしてはそういう感じなので、今までのコンポーネントに対するメンタルモデルを少し変えてみましょう。

以上が実装例です。Selected コンポーネントのrender propをいじるだけで簡単に「選択された要素だけスタイルを変える」振る舞いを様々なコンポーネントに適用できるようになりました。

続いてScoped Slotを用いて同じ機能を実装してみたいと思います。

Scoped Slot

Slotとは何ぞやという話はドキュメントに譲るとして、Scoped Slotの何が普通のSlotと違うかと言いますと、その名の通り「Slotの配信先のコンポーネントのスコープのデータにアクセスすることができる」という点ですね。
こちらも実装して見たのでご覧ください!

Selected.vue
<template>
    <slot name="selected" :selectedVal="selectedVal" :select="select">
      デフォルトの内容。
    </slot>
</template>
<script>
export default {
  name: "Selected",
  data() {
    return { selectedVal: 0 };
  methods: {
    select(value) {
      this.selectedVal = value;
</script>
        slot="selected"
        slot-scope="{selectedVal, select}"> <!-- ここでSelectedコンポーネントのスコープにある変数・関数にアクセスしています -->
          v-for="(item, index) in props.items"
          :class="selectedVal == index ? 'selected-item' : ''"
          @click="select(index)"
          {{ item }}
    </Selected>
</template>
<script>
  import Selected from './Selected';
  export default {
    name: 'hello',
    components:{
      Selected
</script>

Scoped Slotでも配信先のコンポーネントの変数・関数を扱えるため、それを用いて振る舞いだけを抽出することができることを確認しました。ラップされたコンポーネントのデータや関数にアクセスして振る舞いを追加するというのは、やってることはRender Propと同じだなと思います。

実装例作ってみての結論ですが、どちらのパターンもEvan Youが言っている通り解いてる課題もできることも一緒だなと感じました。どっちが良いとかではなく書き方の違いでしかないので、描画関数を使っているか否かなど、携わっているVueプロジェクトのスタイルに応じて使い分ければ良いのではと思います。

それでは、本記事は以上となります。お読みいただきありがとうございました。

  • Use a Render Prop!
  • Use a Vue.js Render Prop!
  • Getting Your Head Around Vue.js Scoped Slots
  • Understanding scoped slots in Vue.js
  • 109
    85
    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
    109
    85