Иногда возникает необходимость использовать данные дочернего компонента в содержимом родительского компонента, которое передается в дочерний. Что-то наподобие:
<div id="app"> <user> <h3>Данные о пользователе</h3> <p>Имя: {{ user.name }}</p> <p>Возраст: {{ user.age }}</p> </user> </div> <script src="https://unpkg.com/vue"></script> <script> const app = Vue.createApp({}); app.component('user', { data(){ return { user:{ name: 'Tom', age: 36 }} }, template: `<div><slot></slot></div>` }); app.mount('#app'); </script>
Данный пример, конечно, не будет работать, потому что для родительского компонента - в данном случае объекта приложения Vue объект user не существует, соответственно мы не можем обратиться к его свойствам name и age. Для решения этой задачи воспользуемся слотами с ограниченной областью видимости.
Слоты с ограниченной областью видимости (scoped slots) позволяют передавать данные между слотом и родительским компонентом. Подобные слоты определяют шаблон, в который можно передавать данные из дочернего компонента. И эти данные ограничены слотом.
Для определения шаблона в родительском компоненте применяется элемент <template>
,
в котором устанавливается специальный атрибут v-slot:имя_слота="данные". Значение этого атрибута - название объекта с данными,
которые передаются от дочернего компонента.
Например, определим следующую веб-страницу:
<!DOCTYPE html> <html> <head> <title>Слоты Vue 3</title> <meta charset="utf-8" /> </head> <body> <div id="app"> <user> <template v-slot:default="props"> <h3>Данные о пользователе</h3> <p>Имя: {{ props.userinfo.name }}</p> <p>Возраст: {{ props.userinfo.age }}</p> </template> </user> </div> <script src="https://unpkg.com/vue"></script> <script> const app = Vue.createApp({}); app.component('user', { data(){ return { user:{ name: 'Tom', age: 36 }} }, template: `<div> <slot v-bind:userinfo="user"></slot> </div>` }); app.mount('#app'); </script> </body> </html>
К примеру, дочерний компонент через слот передает данные пользователя:
<slot v-bind:userinfo="user"></slot>
Здесь устанавливается привязка. То есть через слот в родительский компонент мы передаем объект user. В родительском компоненте мы можем получить этот объект через userinfo.
Для слота с ограниченной областью видимости в родительском компоненте определен шаблон:
<template v-slot:default="props"> <h3>Данные о пользователе</h3> <p>Имя: {{ props.userinfo.name }}</p> <p>Возраст: {{ props.userinfo.age }}</p> </template>
В дочернем компоненте для слота не указано имя, поэтому по умолчанию его имя будет "default". Поэтому для получения данных из дочернего
компонента применяется атрибут v-slot:default
, то есть v-slot:название_слота. Значение этого атрибута props
,
указывает, что внутри шаблона все переданные данные мы сможем получить через ссылку props. Далее внутри шаблона мы можем обратиться к переданным данным:
props.userinfo.name
.
Другой показательный пример представляет использование списков, где родительский компонент может определить шаблон для элемента списка:
<!DOCTYPE html> <html> <head> <title>Слоты Vue 3</title> <meta charset="utf-8" /> </head> <body> <div id="app"> <userslist> <template v-slot:userdetails="props"> <div> <p>Имя: {{ props.userinfo.name}}</p> <p>Возраст: {{ props.userinfo.age }}</p> </div> </template> </userslist> </div> <script src="https://unpkg.com/vue"></script> <script> const app = Vue.createApp({}); app.component('userslist', { data(){ return { users: [ {name:'Tom', age: 36}, {name:'Sam', age: 39}, {name:'Bob', age: 25} ] } }, template: `<ul> <li v-for="user in users"> <slot name="userdetails" v-bind:userinfo="user"></slot> </li> </ul>` }); app.mount('#app'); </script> </body> </html>
В данном случае компонент userslist выводит список users. Для каждого элемента из этого списка создается свой элемент slot:
<slot name="userdetails" v-bind:userinfo="user"></slot>
Для слота определяется одно свойство - userdetails. Каждый объект user из массива передается в родительский компонент через ссылку userinfo.
В родительском компоненте определяется шаблон, в котором выводится каждый объект user:
<template v-slot:userdetails="props"> <div> <p>Имя: {{ props.userinfo.name}}</p> <p>Возраст: {{ props.userinfo.age }}</p> </div> </template>
Причем в данном случае родительский компонент сам определяет шаблон для элементов списка и правила их стилизации.