В Vue 3 есть два основных способа установить связь между дочерними и родительскими объектами. Использование реквизита и использование хранилища Vuex. В этом уроке мы рассмотрим оба ниже.
Общение ребенка с родителями с помощью реквизита
Первый способ, которым дочерние компоненты могут взаимодействовать со своими родительскими компонентами без использования дополнительных узловых пакетов, — это использовать функцию props платформы Vue. Возможно, вы использовали props раньше для передачи данных от родителя к дочернему, но с несколькими небольшими изменениями мы можем передать изменения данных обратно родителю.
То, что вы, возможно, уже знаете о реквизитах для передачи данных от родителя к дочернему, вероятно, выглядит примерно так:
<child :counter="counter" />
Теперь, если мы хотим, чтобы значение counter реагировало на то, что происходит в дочернем элементе, мы можем сделать это, просто добавив v-model к реквизиту следующим образом.
<child v-model:counter="counter" />
Допустим, ребенку может понадобиться что-то сделать со значением счетчика. Ваш код может выглядеть примерно так
<template>
<div>Child Counter: {{ counter }} </div>
<button @click="increment">Increment Counter</button>
</template>
<script>
export default {
props: {
counter: {
type: Number
}
},
data: () => ({
childCounter: 0
}),
created() {
// the prop needs to be re-assigned here. Vue doesn't like prop mutation
this.childCounter = this.counter
},
methods: {
increment() {
this.childCounter++
}
}
}
</script>
Но это не обновляет значение счетчика, о котором знает родитель. Так что же нам делать?
Мы уже сказали дочернему компоненту в родительском компоненте прослушивать обновления для счетчика с помощью v-model . Чтобы подключиться к этому слушателю, мы изменим наш метод приращения с помощью this.$emit('update:myProp', newPropValue)
increment() {
this.childCounter++
this.$emit('update:counter', this.childCounter)
}
Полный код выглядит так:
Родительский компонент
#Parent.vue
<template>
<div>Parent Counter: {{ counter }}</div>
<child v-model:counter="counter" />
</template>
<script>
import Child from './Child'
export default {
components: {
Child,
},
data: () => ({
counter: 0,
}),
}
</script>
Дочерний компонент
<template>
<div>Child Counter: {{ childCounter }} </div>
<button @click="increment">Increment Counter</button>
</template>
<script>
export default {
props: {
counter: {
type: Number
}
},
data: () => ({
childCounter: 0
}),
created() {
// the prop needs to be re-assigned here. Vue doesn't like prop mutation
this.childCounter = this.counter
},
methods: {
increment() {
this.childCounter++
this.$emit('update:counter', this.childCounter)
}
}
}
</script>
Взаимодействие ребенка и родителя с Vuex Store
Использование props в компонентах — это всего лишь один из способов, с помощью которого ребенок может передавать изменения данных родителям. Vue предлагает нам еще один способ достичь этой цели с помощью Vuex Store. Хранилище — это место, где данные хранятся в течение всего сеанса в нашем приложении. Чтобы начать работу с Vuex add, вам нужно добавить его в свое приложение. Нам нужно добавить vuex@next в наш проект, а не vuex, потому что vuex@next работает с Vue 3, а vuex все еще работает с Vue 2 на момент написания этой статьи.
$>npm i vuex@next
Затем нам нужно настроить наш магазин в приложении. Сначала добавим файл сценария магазина. Мы добавим к этому позже
#src/store/store.js
import { createStore } from 'vuex'
export default createStore({})
А затем зарегистрируйте магазин с вашим приложением
#src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store/store'
const app = createApp(App)
app.use(store)
app.mount('#app')
Как только это будет сделано, нам понадобится state со значением, которое содержит counter, а также способ mutate этого состояния.
import { createStore } from 'vuex'
const state = {
counter: 0
}
const mutations = {
increment: (state) => state.counter++
}
export default createStore({
state,
mutations
})
Чтобы получить это значение из хранилища, достаточно добавить в компонент вычисляемое свойство, которое возвращает значение счетчика в хранилище. Это может быть где угодно в приложении, где вам нужно значение, будь то родитель дочернего элемента, выполняющего мутацию, или совершенно другое место.
computed: {
counter() {
return store.state.counter
}
}
И чтобы увеличить счетчик, нам просто нужно commit a mutation в методе вашего компонента
methods: {
increment() {
store.commit('increment')
}
}
Результирующий код будет выглядеть так:
Дочерний компонент
<template>
<div>Child Counter: {{ counter }} </div>
<button @click="increment">Increment Counter</button>
</template>
<script>
import store from '../store/store'
export default {
computed: {
counter() {
return store.state.counter
}
},
methods: {
increment() {
store.commit('increment')
}
}
}
</script>
Родительский компонент
<template>
<div>Parent Counter: {{ counter }}</div>
<child />
</template>
<script>
import store from '../store/store'
import Child from './Child'
export default {
components: {
Child,
},
computed: {
counter() {
return store.state.counter
}
}
}
</script>
Вывод
Мы изучили два основных способа взаимодействия компонентов в Vue 3. Конечно, есть и другие решения и разные реализации магазина. Вы должны найти тот, который лучше всего подходит для вас и задачи, которую вы пытаетесь выполнить.
Мой исходный код, включая приведенные выше примеры, можно найти здесь: https://github.com/jacobtshirt/vue-child-parent-commucation
Документы Vue: https://v3.vuejs.org/
Документы Vuex: https://next.vuex.vuejs.org/