8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Vue.js]非推奨になったslot-scopeの代わりにv-slotを使う

Last updated at Posted at 2019-05-15

slot-scope

子コンポーネントから親コンポーネントにプロパティを渡すための構文。
ScopedSlotsとこれを用いることでデータの振る舞いと見た目を分離することができる。

TODOアプリ

簡単なTODOアプリのコードを例に使い方を見ていきます。
qiita-vue-gif.gif

Components

Parent Component

  • App.vue   ← 全てのTodoを描画する

Child Component

  • Todo.vue  ← Todoの見た目を定義
  • TodosData.vue  ← TodoDataの振る舞いを定義

slot-scopeとv-slot

Version 2.5以前はslot-scope
Version 2.6以降はslot-scopeとv-slot
が用意されている。
Vue 3.xからはslot-scopeはなくなるかもしれないので注意。

共通(Todo.vue, TodosData.vue)

Todo.vue
<script>
export default {
  props: {
    todo: String,
    required: true,
    default: ''
  },
  methods: {
    handleTodoDone () {
      this.$emit('todoDone')
    }
  }
}
</script>

<template>
  <div>
    <span>
      {{ todo }}
    </span>
    <button @click="handleTodoDone">
      Done
    </button>
  </div>
</template>
TodosData.vue
<script>
export default {
  data () {
    return {
      todos: [],
      newTodoText: ''
    }
  },
  methods: {
    handleTodoDone (time) {
      for (let i = 0; i < this.todos.length; i++) {
        if (this.todos[i].time === time) this.todos.splice(i, 1)
      }
    },
    handleTodoAdd () {
      if (this.newTodoText === '') return
      const time = this.getTime()
      const newTodo = { text: this.newTodoText, time: time }
      this.todos.push(newTodo)
      this.newTodoText = ''
    },
    handleNewTodoChange (newTodo) {
      this.newTodoText = newTodo
    },
    getTime () {
      const now = new Date()
      const hour = String(now.getHours())
      const minute = String(now.getMinutes())
      const second = String(now.getSeconds())
      const time = hour + minute + second
      return time
    }
  },
  render (createElement) {
    return this.$scopedSlots.default({
      todos: this.todos,
      todoDone: this.handleTodoDone,
      todoAdd: this.handleTodoAdd,
      newTodoText: this.newTodoText,
      newTodoChange: this.handleNewTodoChange
    })[0]
  }
}
</script>

scopedSlotsを使うことで無駄なタグを生成せずに済む。

Vue 2.5以前

App.vue
<script>
export default {
  components: { Todo, TodosData }
}
</script>

<template>
  <div>
    <todos-data>
      <template slot-scope="todosData">
        <div>
          <div>
            <input
              :value="todosData.newTodoText"
              type="text"
              @change="(e) => todosData.newTodoChange(e.target.value)"
            >
            <button @click="todosData.todoAdd">
              ADD
            </button>
          </div>
          <template v-for="todo in todosData.todos">
            <todo
              :todo="todo.text"
              :key="todo.time"
              @todoDone="todosData.todoDone(todo.time)"
            />
          </template>
        </div>
      </template>
    </todos-data>
  </div>
</template>

slot-scope 構文を用いてスロットプロパティを受け渡すには、親コンポーネントは <template> に対して slot-scope 属性を使う必要があります。

Vue 2.6以降

App.vue
<script>
export default {
  components: { Todo, TodosData }
}
</script>

<template>
  <div>
    <todos-data v-slot="todosData">
      <div>
        <div>
          <input
            :value="todosData.newTodoText"
            type="text"
            @change="(e) => todosData.newTodoChange(e.target.value)"
          >
          <button @click="todosData.todoAdd">
            ADD
          </button>
        </div>
        <template v-for="todo in todosData.todos">
          <todo
            :todo="todo.text"
            :key="todo.time"
            @todoDone="todosData.todoDone(todo.time)"
          />
        </template>
      </div>
    </todos-data>
  </div>
</template>

v-slot 構文では、デフォルトスロットだけの場合はコンポーネントのタグをスロットのテンプレートとして使うことができます。
つまり、上記のように TodosData に記述することができます。

え、一緒じゃん?

はい、書く位置が変わっただけでやっていることは変わっていません。
そもそもdeprecatedになった理由が、
「コンポーネントがテンプレート内でどのプロパティを提供しているのかパッと見分かりにくい」
というものだったので当然と言えば当然ですが。

終わりに

意外とまだslot-scopeを見かけるのでまとめてみました。
Vue.js楽しい!

補足

  1. CSSは省略しています(というか色々端折ってます)
  2. Vue公式
  3. RFCs
8
10
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
8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?