理系学生日記

おまえはいつまで学生気分なのか

Vue.jsのslot-scopeは振舞いを抽出するのに便利なのか

Vue.js の slot-scope、なかなか理解できなかったのですが、ようやく得心に至りました。 slot-scope、振舞いを抽出するのに便利なのか。

TODOリストの個々の要素への処理

たとえば、ここで todo-list というコンポーネントがあるとします。 このコンポーネントの責務は、「個々の TODO をループさせ、その個々の要素に処理をすること」になります。 (デフォルト処理は"出力する"ようにしてます)

Vue.component('todo-list', {
    props: {
        todos: {
            type: Array,
            required: true
        }
    },
    template: `
<ul>
  <template v-for="todo in todos">
    <slot :todo="todo">
      <li :key="todo.id">
        {{ todo.text }}
      </li>
    </slot>
  </template>
</ul>
`
})

実際に、以下のようなデータでこのコンポーネントを実行してみると、 全データがリスト出力されることがわかります。

 <div id="todo-list-demo1">
   <todo-list :todos="todos"></todo-list>
 </div>
[
    { id: 1, text: 'C++',        isCompleted: true   },
    { id: 2, text: 'JavaScript', isCompleted: false  },
    { id: 3, text: 'Java',       isCompleted: true   },
    { id: 4, text: 'Perl',       isCompleted: false  }
]

実行例

完了済の TODO のみ出力

では、次に完了済み TODO のみ出力したいという場合、また新しく一からコンポーネントを作るという方法もあるのですが、 既述の通り、todo-list の責務は 「個々の TODO をループさせ、その個々の要素に処理をすること」にあるので、 ここでの「処理」を「isCompleted が true の要素のみ出力する」に変えてしまえば良い。

これは todo-list を一切触れることなく実現が可能です。 HTML の側に以下のように表記すれば良い。

    <div id="todo-list-demo2">
      <todo-list :todos="todos">
        <li slot-scope="{ todo }" v-if="todo.isCompleted">
          {{ todo.text }}
        </li>
      </todo-list>
    </div>

ここでは、<li> タグとともに v-if という条件の制御構造を todo-list の body に加えています。 この body は、todo-list の以下の <slot> と対応します。

    <slot :todo="todo">
      <li :key="todo.id">
        {{ todo.text }}
      </li>
    </slot>

実行例

  • {{ todo.text }}
  • というわけで

    scope-slot を使えば、コンポーネントを使う側で、コンポーネントの動作をコントロールすることが可能になります。 React でいうと Render Prop のようなもの。