Руководство для начинающих по работе с компонентами во Vue
В этом руководстве мы рассмотрим работу с компонентами во Vue. Я расскажу, как создавать компоненты, как передавать данные между компонентами. А также как использовать элемент <slot> для рендеринга дополнительного контента внутри компонента.
Как создавать компоненты во Vue
Компоненты - это многократно используемые экземпляры Vue. Существуют различные способы создания компонентов в приложении. Например, для регистрации глобального компонента можно использовать метод Vue.component:
Vue.component('my-counter', {
data() {
return {
count: 0
}
},
template: `<div>{{ count }}</div>`
})
new Vue({ el: '#app' })
Имя компонента - my-counter. Его можно использовать следующим образом:
<div id="app">
<my-counter></my-counter>
</div>
В приведенном выше примере data - это функция, которая возвращает литерал объекта. Благодаря этому каждый экземпляр компонента получает собственный объект data.
Есть несколько способов определения шаблон компонента. В предыдущем примере используется литерал объекта. Но мы также можем использовать тег <script>, встроенный в DOM шаблона.
Однофайловые компоненты
В более сложных проектах глобальные компоненты становятся громоздкими. В таких случаях лучше использовать однофайловые компонентов. Это отдельные файлы с расширением .vue, которые включают в себя разделы <template>, <script> и <style>.
Компонент App может выглядеть следующим образом:
<template>
<div id="app">
<my-counter></my-counter>
</div>
</template>
<script>
import myCounter from './components/myCounter.vue'
export default {
name: 'app',
components: { myCounter }
}
</script>
<style></style>
А компонент MyCounter может выглядеть так:
<template>
<div>{{ count }}</div>
</template>
<script>
export default {
name: 'my-counter',
data() {
return {
count: 0
}
}
}
</script>
<style></style>
Однофайловые компоненты можно импортировать и использовать непосредственно в тех компонентах, где они необходимы.
В этом руководстве я представлю все примеры с использованием метода регистрации компонента Vue.component().
Передача данных в компоненты через свойства
Свойства позволяют передавать данные из родительского компонента в дочерний. Это дает возможность сделать компоненты более мелкими, чтобы обрабатывать определенные функции.
Предположим, что в компонент блога выводит сведения об авторе, публикации (заголовок, текст и изображения) и комментарии. Но его можно разбить на дочерние компоненты, каждый из которых отвечает за вывод определенной информации:
<BlogPost>
<AuthorDetails></AuthorDetails>
<PostDetails></PostDetails>
<Comments></Comments>
</BlogPost>
При этом все данные будут переданы из родительского компонента. Он может выглядеть следующим образом:
new Vue({
el: '#app',
data() {
return {
author: {
name: 'John Doe',
email: 'jdoe@example.com'
}
}
}
})
В упомянутом выше компоненте мы определили данные автора и информацию о посте. Далее нужно создать дочерний компонент. Назовем его author-detail. HTML-шаблон будет выглядеть следующим образом:
<div id="app">
<author-detail :owner="author"></author-detail>
</div>
Мы передаем дочернему компоненту author объект как свойство с именем owner. В дочернем компоненте owner- это имя свойства, с помощью которого мы получаем данные из родительского компонента. Данные, которые мы хотим получить, называются author. Их мы определили в родительском компоненте.
Чтобы получить доступ к этим данным, нужно объявить свойство в компоненте author-detail:
Vue.component('author-detail', {
template: `
<div>
<h2>{{ owner.name }}</h2>
<p>{{ owner.email }}</p>
</div>
´,
props: ['owner']
})
Также можно включить валидацию при передаче свойства, чтобы убедиться, что отображаются правильные данные. Чтобы включить валидацию в приведенном выше примере, измените компонент следующим образом:
Vue.component('author-detail', {
template: `
<div>
<h2>{{ owner.name }}</h2>
<p>{{ owner.email }}</p>
</div>
`,
props: {
owner: {
type: Object,
required: true
}
}
})
Если мы передадим неправильный тип свойства, в консоли отобразится следующая ошибка:
"[Vue warn]: Invalid prop: type check failed for prop 'text'. Expected Boolean, got String.
(found in component <>)"
В документации Vue доступно руководство по валидации свойств.
Передача данных от дочернего компонента в родительский через шину событий
События обрабатываются через методы-оболочки, которые запускаются, когда происходит выбранное событие. Давайте расширим пример счетчика, чтобы он увеличивался при каждом нажатии кнопки.
Вот как должен выглядеть компонент:
new Vue({
el: '#app',
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
})
Код шаблона:
<div id="app">
{{ count }}
<div>
<button @click="increment">+</button>
</div>
</div>
Мы подписываемся на событие onClick, чтобы вызывать метод increase при каждом нажатии кнопки. Затем метод increase увеличивает значение переменной count.
Теперь расширим пример, чтобы переместить кнопку счетчика в отдельный компонент и отобразить счетчик в родительском элементе. Мы можем сделать это с помощью шины событий.
Шины событий полезны, когда нужно реализовать взаимодействие дочернего компонента с родительским. Вы можете использовать шину событий, если приложение недостаточно велико, чтобы для него требовалось использование Vuex.
Что мы хотим сделать: объект count будет объявлен в родительском компоненте и передан дочернему. Затем в дочернем компоненте увеличивается значение count, а также обновляется значение в родительском компоненте.
Компонент приложения будет выглядеть следующим образом:
new Vue({
el: '#app',
data() {
return {
count: 0
}
}
Затем в дочернем компоненте с помощью свойства получаем значение счетчика и реализуем метод для его увеличения. Мы будем отображать значение count в родительском компоненте:
Vue.component('counter', {
template: `
<div>
<button @click="increment">+</button>
</div>
`,
props: {
value: {
type: Number,
required: true
}
},
methods: {
increment() {
this.count++
}
}
})
Тогда наш шаблон будет выглядеть следующим образом:
<div id="app">
<h3>
{{ count }}
</h3>
<counter :count="count" />
</div>
Чтобы это сработало, необходимо отправить событие из дочернего компонента, новое значение count и прослушать это событие в родительском компоненте.
Сначала мы создаем новый экземпляр Vue и присваиваем его константе eventBus:
const eventBus = new Vue();
Теперь мы можем использовать шину событий в компоненте. Дочерний компонент будет выглядеть следующим образом:
Vue.component('counter', {
props: {
count: {
type: Number,
required: true
}
},
methods: {
increment() {
this.count++
eventBus.$emit('count-incremented', this.count)
}
},
template: `
<div>
<button @click="increment">+</button>
</div>
`
})
Событие генерируется каждый раз, когда вызывается метод increment. Нужно прослушать событие в главном компоненте, а затем установить значение count, которое мы получили через отправленное событие:
new Vue({
el: '#app',
data() {
return {
count: 0
}
},
created() {
eventBus.$on('count-incremented', (count) => {
this.count = count
})
}
})
Обратите внимание, что мы используем метод жизненного цикла Vue created для подключения к компоненту до его объявления, а также для настройки шины событий.
Использовать шину событий рекомендуется, если приложение не сложное.
Вложение содержимого в компоненты с использованием слотов
Если вы попробуете использовать компонент с закрывающимся тегом и поместите внутрь него содержимое, то Vue заменит его выводом самого компонента:
<div id="app">
<author-detail :owner="author">
<p>This will be replaced</p>
</author-detail>
</div>
К счастью, слоты Vue позволяют передавать компоненту произвольное значение. Это может быть что угодно: от элементов DOM до родительского и дочернего компонентов. Давайте посмотрим, как они работают.
Часть скрипта компонентов будет выглядеть следующим образом:
Vue.component('list', {
template: '#list'
})
new Vue({
el: "#app"
})
Тогда шаблоны будут выглядеть так:
<div id="app">
<h2>Slots</h2>
<list>
<h4>I am the first slot</h4>
</list>
<list>
<h4>I am the second slot</h4>
</list>
</div>
<script type="text/x-template" id="list">
<div>
<h3>Child Component</h3>
<slot></slot>
</div>
</script>
Содержимое компонента <list> отображается между тегами <slot></slot>. Мы также можем использовать запасной контент.
<div id="app">
<h2>Slots</h2>
<list>
<h4>I am the first slot</h4>
</list>
<list></list>
</div>
<script type="text/x-template" id="list">
<div>
<h3>Child Component</h3>
<slot>This is fallback content</slot>
</div>
</script>
Резервный контент будет отображаться в тех случаях, когда родительский компонент не будет возвращать данные для отображения.
Заключение
В этой статье мы рассмотрели, как создавать компоненты в Vue, передавать данные от родительского к дочернему компоненту через свойства и шину событий. Также мы рассмотрели слоты – удобный метод для составления компонентов наиболее эффективными способами. Надеюсь, это руководство было для вас полезным.