Как передавать данные между компонентами в Vue.js
Опубликовано: 2022-03-10Совместное использование данных между компонентами — одна из основных функций VueJS. Это позволяет вам разрабатывать более модульный проект, управлять областями данных и создавать естественный поток данных в вашем приложении.
Если вы не создаете все свое приложение Vue в одном компоненте (что не имеет никакого смысла), вы столкнетесь с ситуациями, когда вам нужно обмениваться данными между компонентами.
К концу этого урока вы будете знать три способа сделать это.
- Использование реквизита для обмена данными от родителя к дочернему,
- Генерация пользовательских событий для обмена данными от дочернего к родительскому,
- Использование Vuex для создания общего состояния на уровне приложения.
Хорошо — приступим!
Создание приложения с помощью Nuxt
С помощью Spotify ваши друзья могут проверить, под что вы играете. Что, если остальная часть Интернета тоже сможет испытать на себе ваш алгоритм? Узнайте, как создать собственное приложение, чтобы делиться тем, что вы слушаете, на Spotify с помощью Vue.js и Nuxt. Читать статью по теме →
1. Использование реквизита для обмена данными от родителя к дочернему
Реквизиты VueJS — это самый простой способ обмена данными между компонентами. Реквизиты — это настраиваемые атрибуты, которые мы можем присвоить компоненту. Затем в нашем шаблоне мы можем задать значения этих атрибутов и — БАМ — мы передаем данные от родительского к дочернему компоненту!
Например, предположим, что мы работаем над страницей профиля пользователя и хотим, чтобы дочерний компонент принимал реквизит имени пользователя. Нам понадобятся два компонента.
- Дочерний компонент, принимающий реквизит, назовем его
AccountInfo.vue
. - Родительский компонент, передающий реквизит, назовем его
ProfilePage.vue
.
Внутри AccountInfo.vue
мы можем объявить реквизиты, которые он принимает, используя параметр props. Итак, внутри опций компонента давайте сделаем так, чтобы это выглядело следующим образом.
// AccountInfo.vue <template> <div id='account-info'> {{username}} </div> </template> <script> export default { props: ['username'] } </script>
Затем, чтобы фактически передать данные от родителя ( ProfilePage.vue
), мы передаем их как настраиваемый атрибут.
// ProfilePage.vue <account-info username='matt' />
Теперь, если мы загрузим нашу страницу, мы увидим, что наш компонент AccountInfo
правильно отображает значение, переданное его родителем.
Как и при работе с другими директивами VueJS, мы можем использовать v-bind для динамической передачи свойств. Например, предположим, что мы хотим установить свойство имени пользователя равным переменной. Мы можем добиться этого, используя сокращение для директивы v-bind (или просто :
для краткости). Код будет выглядеть примерно так:
<template> <div> <account-info :username="user.username" /> </div> </template> <script> import AccountInfo from "@/components/AccountInfo.vue"; export default { components: { AccountInfo }, data() { return { user: { username: 'matt' } } } } </script>
Это означает, что мы можем изменить наши данные, и любые дочерние реквизиты, использующие это значение, также будут обновляться.
Совет: всегда проверяйте реквизиты
Если вы хотите написать более понятный код Vue, важным методом является проверка ваших реквизитов. Короче говоря, это означает, что вам нужно указать требования к вашему реквизиту (т. е. тип, формат и т. д.). Если одно из этих требований не выполняется (например, если свойству передан неверный тип), Vue выведет предупреждение.
Допустим, мы хотим, чтобы наша поддержка имени пользователя принимала только строки. Нам нужно изменить наш объект реквизита, чтобы он выглядел так:
export default { props: { username: String } }
Проверка свойств необходима при работе с крупномасштабными приложениями Vue или при разработке плагинов. Это помогает убедиться, что все находятся на одной странице, и использовать реквизит так, как он был задуман.
Для получения полного списка проверок, которые мы можем включить в свойства, я определенно рекомендую ознакомиться с официальной документацией для углубленного обзора.
Совет: следуйте соглашениям об именах объектов
Согласно руководству по стилю VueJS, лучший способ назвать ваши реквизиты — это использовать camelCase
при объявлении их в вашем скрипте и kebab-case при ссылке на них в коде шаблона.
Причина этого на самом деле довольно проста. В Javascript camelCase
является стандартным соглашением об именах, а в HTML — kebab-case.
Итак, Vue рекомендует придерживаться норм каждого языка. К счастью, Vue может автоматически преобразовывать два стиля, поэтому разработчикам не требуется дополнительная настройка.
// GOOD <account-info :my-username="user.username" /> props: { myUsername: String } // BAD <account-info :myUsername="user.username" /> props: { "my-username": String }
2. Генерация событий для обмена данными от дочернего к родительскому
Теперь, когда у нас есть данные, передаваемые по иерархии, давайте передадим их другим путем: от дочернего компонента к родительскому. Мы не можем использовать реквизит, но можем использовать пользовательские события и прослушиватели.
Каждый экземпляр Vue может вызвать метод .$emit(eventName)
, который запускает событие. Затем мы можем прослушивать это событие так же, как и любое другое, используя директиву v-on.
Создание пользовательского события
Давайте продолжим наш пример с профилем пользователя, добавив кнопку, которая меняет имя пользователя. Внутри нашего дочернего компонента ( AccountInfo.vue
) давайте создадим кнопку.
Затем, когда эта кнопка будет нажата, мы создадим событие с именем changeUsername
.
<template> <div id='account-info'> <button @click='changeUsername()'>Change Username</button> {{username}} </div> </template> <script> export default { props: { username: String }, methods: { changeUsername() { this.$emit('changeUsername') } } } </script>
Внутри родителя мы обрабатываем это событие и меняем переменную user.username
. Как мы обсуждали ранее, мы можем прослушивать события, используя директиву v-on или «@» для краткости.
<template> <div> <account-info :username="user.username" @changeUsername="user.username = 'new name'"/> </div> </template>
Давайте попробуем. Вы должны увидеть, что когда вы нажимаете кнопку, имя пользователя меняется на «новое имя».
Совет: Пользовательские события могут принимать аргументы
Наиболее распространенный вариант использования передачи аргументов вашим событиям — это когда вы хотите, чтобы дочерний компонент мог установить определенное значение для своей опоры. Вы никогда не захотите напрямую редактировать значение реквизита из самого компонента.
Однако, к счастью, мы можем использовать аргументы передачи с нашими пользовательскими событиями, чтобы изменить значения родительского компонента.
Допустим, мы хотим изменить событие changeUsername
, чтобы мы могли передать ему значение.
Метод $emit
принимает необязательный второй параметр в качестве аргументов. Итак, все, что мы делаем, это добавляем новое значение имени пользователя после названия нашего события.
this.$emit('changeUsername', 'mattmaribojoc')
Затем в нашем родительском компоненте мы можем либо получить встроенный доступ к этим значениям, используя специальную переменную $event
, либо написать метод-обработчик, который принимает параметр.
<account-info :username="user.username" @changeUsername="user.username = $event"/> OR <account-info :username="user.username" @changeUsername="changeUsername($event)"/> export default { ... methods: { changeUsername (username) { this.user.username = username; } } }
3. Использование Vuex для создания общего состояния на уровне приложения
Хорошо — мы знаем, как обмениваться данными между родителями/детями, но как насчет других компонентов? Нужно ли нам создавать чрезвычайно сложную систему иерархии, если мы хотим передавать данные?
К счастью, нет. Замечательная библиотека управления состоянием Vuex годами упрощала жизнь разработчикам. Короче говоря, он создает централизованное хранилище данных, доступное для всех компонентов.
В методах, которые мы использовали ранее (props/Emitting Events), каждый компонент имеет свое собственное состояние данных, которое мы затем разделяем между компонентами. Однако Vuex позволяет нам извлекать все общие данные в единое состояние, к которому каждый компонент может легко получить доступ. Это общее состояние называется хранилищем.
Давайте попробуем.
Поскольку Vuex отделен от основного кода Vue, нам сначала нужно установить и импортировать его в наш проект. Во-первых, нам нужно запустить npm install vuex --save
внутри CLI нашего проекта.
Затем создайте папку src/store с файлом index.js, который содержит следующий код.
// store/index.js import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); export default new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {} });
Чтобы включить это в наш корневой экземпляр Vue, мы должны импортировать наш файл store/index.js и передать его в наш конструктор Vue.
// main.js import store from "./store"; new Vue({ store, ...
Доступ к внутренним компонентам Vue Store
Поскольку мы добавили наше хранилище Vuex в наш корневой экземпляр Vue, оно внедряется во все дочерние элементы корня. Если мы хотим получить доступ к хранилищу из компонента, мы можем использовать this.$store
.
Теперь давайте углубимся в особенности каждой из четырех частей магазина Vuec.
1. Состояние
Состояние Vuex — это объект, содержащий данные уровня приложения. Все экземпляры Vue смогут получить доступ к этим данным.
Для нашего магазина давайте создадим объект пользователя, в котором будут храниться дополнительные данные профиля пользователя.
export default new Vuex.Store({ state: { user: { username: 'matt', fullName: 'Matt Maribojoc' } }, getters: {}, mutations: {}, actions: {} });
Мы можем получить доступ к этим данным внутри любого компонента экземпляра, подобного этому.
mounted () { console.log(this.$store.state.user.username); },
2. Добытчики
Мы используем геттеры Vuex для возврата измененного значения данных состояния. Хороший способ думать о геттерах — обращаться с ними как с вычисляемыми свойствами. Например, геттеры, как и вычисляемые свойства, кэшируют свои результаты и переоценивают только при изменении зависимости.
Опираясь на наш более ранний магазин, допустим, мы хотим создать метод, который возвращает имя пользователя на основе атрибута полного имени.
getters: { firstName: state => { return state.user.fullName.split(' ')[0] } }
Свойства геттера Vuex доступны для компонентов в объекте store.getters
.
mounted () { console.log(this.$store.getters.firstName); }
Совет: знайте аргументы геттера по умолчанию
По умолчанию геттеры Vuex принимают два аргумента.
- state — объект состояния нашего приложения;
- геттеры — объект store.getters, означающий, что мы можем вызывать другие геттеры в нашем магазине.
Каждому объявленному вами геттеру потребуется первый аргумент состояния. И в зависимости от того, как вы разрабатываете свой код, ваши геттеры могут ссылаться друг на друга, используя второй аргумент «геттеры».
Давайте создадим получатель фамилии, который просто удалит значение нашего имени из нашего свойства состояния полного имени. В этом примере потребуются как объекты состояния, так и геттеры.
lastName (state, getters) { return state.user.fullName.replace(getters.firstName, ''); }
Совет: передайте пользовательские аргументы в Vuex Getters
Еще одна интересная особенность геттеров заключается в том, что мы можем передавать им пользовательские аргументы, заставляя наш геттер возвращать метод.
prefixedName: (state, getters) => (prefix) => { return prefix + getters.lastName; } // in our component console.log(this.$store.getters.prefixedName("Mr."));
3. Мутации
Мутации — единственный способ правильно изменить значение объекта состояния. Важно отметить, что мутации должны быть синхронными .
Как и геттеры, мутации всегда принимают свойство состояния Vuex в качестве своего первого аргумента. Они также принимают пользовательский аргумент, называемый полезной нагрузкой, в качестве второго аргумента.
Например, давайте сделаем мутацию, чтобы изменить имя пользователя на определенное значение.
mutations: { changeName (state, payload) { state.user.fullName = payload } },
Затем мы можем вызвать этот метод из нашего компонента, используя метод store.commit
, с нашей полезной нагрузкой в качестве второго аргумента.
this.$store.commit("changeName", "New Name");
Чаще всего вы захотите, чтобы ваша полезная нагрузка была объектом. Это не только означает, что вы можете передать несколько аргументов в мутацию, но также делает ваш код более читабельным из-за имен свойств в вашем объекте.
changeName (state, payload) { state.user.fullName = payload.newName }
Есть два разных способа вызова мутаций с полезной нагрузкой.
- Вы можете указать тип мутации в качестве первого аргумента и полезную нагрузку в качестве второго.
- Вы можете объявить передать один объект с одним свойством для типа и другим для полезной нагрузки.
this.$store.commit("changeName", { newName: "New Name 1", }); // or this.$store.commit({ type: "changeName", newName: "New Name 2" });
Между тем, как они работают, нет реальной разницы, поэтому все зависит от личных предпочтений. Помните, что всегда лучше быть последовательным на протяжении всего проекта, так что какой бы вариант вы ни выбрали, придерживайтесь его!
4. Действия
В Vuex действия довольно похожи на мутации, потому что мы используем их для изменения состояния. Однако сами действия не изменяют значения. Вместо этого действия совершают мутации.
Кроме того, мутации Vuex должны быть синхронными, а действия — нет. Используя действия, мы можем вызвать мутацию, например, после вызова API.
В то время как большинство обработчиков Vuex, которые мы видели, принимают состояние в качестве своего основного параметра, действия принимают объект контекста. Этот объект контекста позволяет нам получить доступ к свойствам в нашем хранилище Vuex (например, состояние, фиксация, геттеры).
Вот пример действия Vuex, которое ждет две секунды, а затем фиксирует мутацию changeName
.
actions: { changeName (context, payload) { setTimeout(() => { context.commit("changeName", payload); }, 2000); } }
Внутри наших компонентов мы используем метод store.dispatch для запуска нашей функции. Мы передаем аргументы точно так же, как и с мутациями. Мы объявляем тип и передаем любые пользовательские аргументы во втором аргументе.
this.$store.dispatch("changeName", { newName: "New Name from Action" });
Подведение итогов
Теперь вы должны знать три разных способа обмена данными между компонентами в VueJS: свойства, пользовательские события и хранилище Vuex.
Я надеюсь, что это руководство помогло вам лучше понять некоторые различные методы и лучшие практики Vue. Дайте мне знать, как вы внедрили их в свои проекты!
Дальнейшее чтение
Если вы заинтересованы в более глубоком изучении технической стороны/возможностей каждой техники, вот несколько отличных мест для начала.
- Веб-сайт официального руководства Vuex
- Документация VueJS по реквизитам и пользовательским событиям
- «WTF — это Vuex? Руководство для начинающих по хранилищу данных приложений Vue», — Энтони Гор, разработчики Vue.js.