Skip to content

Подробное руководство по использованию Scoped Slots в Vue 3

Scoped slots (локальные слоты) в Vue.js — это мощный инструмент для создания гибких и переиспользуемых компонентов. Они позволяют передавать данные из дочернего компонента в родительский, предоставляя родительскому компоненту контроль над тем, как будет отображаться содержимое слота.

В этом руководстве мы подробно рассмотрим:

  1. Что такое слоты и scoped slots.
  2. Синтаксис и использование в Vue 3.
  3. Различия между Vue 2 и Vue 3 в контексте scoped slots.
  4. Примеры использования.
  5. Практические советы и лучшие практики.

1. Что такое слоты и Scoped Slots

Обычные слоты

Слоты — это места в шаблоне компонента, которые заполняются содержимым, переданным из родительского компонента.

Пример:

vue
<!-- Родительский компонент -->
<template>
  <MyComponent>
    <p>Это содержимое слота.</p>
  </MyComponent>
</template>
vue
<!-- Дочерний компонент (MyComponent.vue) -->
<template>
  <div>
    <slot></slot>
  </div>
</template>

Scoped Slots

Scoped slots позволяют дочернему компоненту передавать данные в слот, которые родительский компонент может использовать.

Зачем нужны Scoped Slots?

Они позволяют:

  • Делать компоненты более гибкими и переиспользуемыми.
  • Давать родительскому компоненту контроль над тем, как отображать данные, предоставленные дочерним компонентом.

2. Синтаксис и использование в Vue 3

В дочернем компоненте

Дочерний компонент предоставляет данные в слот через атрибут v-slot (или просто #), передавая объект в slot с помощью v-bind.

Пример:

vue
<!-- Дочерний компонент (MyComponent.vue) -->
<template>
  <div>
    <slot :user="userData"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      userData: {
        name: 'Иван',
        age: 30,
      },
    };
  },
};
</script>

В родительском компоненте

Родительский компонент принимает данные из слота и использует их в своем шаблоне.

Пример:

vue
<!-- Родительский компонент -->
<template>
  <MyComponent>
    <template #default="slotProps">
      <p>Имя пользователя: {{ slotProps.user.name }}</p>
      <p>Возраст пользователя: {{ slotProps.user.age }}</p>
    </template>
  </MyComponent>
</template>

<script>
import MyComponent from './MyComponent.vue';

export default {
  components: {
    MyComponent,
  },
};
</script>

Пояснение:

  • #default — это сокращенный синтаксис для v-slot:default.
  • slotProps — объект, переданный из дочернего компонента через v-bind.

3. Различия между Vue 2 и Vue 3

Изменения в синтаксисе

  • В Vue 2 использовался синтаксис slot-scope, который был признан громоздким и менее интуитивным.
  • В Vue 3 рекомендуется использовать директиву v-slot или ее сокращение #.

Пример в Vue 2:

vue
<!-- Родительский компонент в Vue 2 -->
<MyComponent>
  <template slot="default" slot-scope="slotProps">
    <!-- использование slotProps -->
  </template>
</MyComponent>

Пример в Vue 3:

vue
<!-- Родительский компонент в Vue 3 -->
<MyComponent>
  <template #default="slotProps">
    <!-- использование slotProps -->
  </template>
</MyComponent>

Удаление slot-scope

  • В Vue 3 slot-scope больше не используется.
  • Вместо этого используется v-slot и его сокращения.

4. Примеры использования

Пример 1: Компонент списка с настраиваемым отображением элементов

Дочерний компонент (ItemList.vue):

vue
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <slot :item="item">
        <!-- Значение по умолчанию, если слот не предоставлен -->
        {{ item.name }}
      </slot>
    </li>
  </ul>
</template>

<script>
export default {
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
};
</script>

Родительский компонент:

vue
<template>
  <ItemList :items="products">
    <template #default="{ item }">
      <strong>{{ item.name }}</strong> - Цена: {{ item.price }} руб.
    </template>
  </ItemList>
</template>

<script>
import ItemList from './ItemList.vue';

export default {
  components: {
    ItemList,
  },
  data() {
    return {
      products: [
        { id: 1, name: 'Товар 1', price: 100 },
        { id: 2, name: 'Товар 2', price: 200 },
      ],
    };
  },
};
</script>

Пояснение:

  • Дочерний компонент передает каждый item в слот.
  • Родительский компонент определяет, как отобразить каждый item.

Пример 2: Форма с настраиваемыми элементами ввода

Дочерний компонент (FormWrapper.vue):

vue
<template>
  <form @submit.prevent="handleSubmit">
    <slot :formData="formData" :updateField="updateField"></slot>
    <button type="submit">Отправить</button>
  </form>
</template>

<script>
export default {
  data() {
    return {
      formData: {},
    };
  },
  methods: {
    updateField(field, value) {
      this.formData[field] = value;
    },
    handleSubmit() {
      this.$emit('submit', this.formData);
    },
  },
};
</script>

Родительский компонент:

vue
<template>
  <FormWrapper @submit="submitForm">
    <template #default="{ formData, updateField }">
      <label>
        Имя:
        <input type="text" @input="updateField('name', $event.target.value)" />
      </label>
      <label>
        Email:
        <input type="email" @input="updateField('email', $event.target.value)" />
      </label>
    </template>
  </FormWrapper>
</template>

<script>
import FormWrapper from './FormWrapper.vue';

export default {
  components: {
    FormWrapper,
  },
  methods: {
    submitForm(data) {
      console.log('Отправка данных формы:', data);
    },
  },
};
</script>

Пояснение:

  • Дочерний компонент предоставляет методы и данные для управления формой.
  • Родительский компонент определяет, какие поля будут в форме и как они будут выглядеть.

5. Практические советы и лучшие практики

Именованные слоты

Вы можете определять несколько слотов с разными именами.

Дочерний компонент:

vue
<template>
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot> <!-- Слот по умолчанию -->
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</template>

Родительский компонент:

vue
<template>
  <MyComponent>
    <template #header>
      <h1>Заголовок</h1>
    </template>
    <template #default>
      <p>Основное содержимое</p>
    </template>
    <template #footer>
      <p>Подвал</p>
    </template>
  </MyComponent>
</template>

Деструктуризация пропсов слота

Вы можете использовать деструктуризацию для удобства.

vue
<template>
  <MyComponent>
    <template #default="{ user: { name, age } }">
      <p>Имя: {{ name }}</p>
      <p>Возраст: {{ age }}</p>
    </template>
  </MyComponent>
</template>

Использование render-функций

В некоторых случаях может быть удобно использовать render-функции для более гибкого управления слотами.

Пример:

javascript
// В родительском компоненте
export default {
  render() {
    return h(MyComponent, {}, {
      default: ({ item }) => h('div', {}, item.name),
    });
  },
};

Предоставление действий через scoped slots

Вы можете передавать не только данные, но и функции.

Дочерний компонент:

vue
<template>
  <div>
    <slot :count="count" :increment="increment"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    increment() {
      this.count++;
    },
  },
};
</script>

Родительский компонент:

vue
<template>
  <Counter>
    <template #default="{ count, increment }">
      <p>Счетчик: {{ count }}</p>
      <button @click="increment">Увеличить</button>
    </template>
  </Counter>
</template>

Заключение

Scoped slots в Vue 3 являются мощным инструментом для создания гибких и переиспользуемых компонентов. Они позволяют компонентам быть более абстрактными, предоставляя при этом возможность настраивать их поведение и отображение из родительских компонентов.

Ключевые моменты:

  • Используйте v-slot или его сокращение # для определения слотов.
  • Передавайте данные из дочернего компонента в родительский через v-bind в <slot>.
  • В родительском компоненте получайте данные через переменные слота в template.

Лучшие практики:

  • Ясно именуйте пропсы слотов, чтобы облегчить понимание кода.
  • Избегайте глубокой вложенности, чтобы не усложнять структуру приложения.
  • Документируйте API ваших компонентов, если они предоставляют слоты, чтобы другие разработчики понимали, как ими пользоваться.

Используя scoped slots, вы сможете создавать более модульные и адаптируемые компоненты, что облегчит разработку и поддержку вашего приложения на Vue.js.