Связь между родственными компонентами в VueJs 2.0

В Вуэйс 2.0model.sync будетосуждается.

Итак, как правильно общаться между родственными компонентами вvuejs 2.0?

Как я ловлюидея в Vue 2.0, чтобы иметь родственное общениес помощью магазина или событийного автобуса.

В соответствии сEvan:

Стоит также упомянуть, что «передача данных между компонентами», как правило, плохая идея, потому что в конце поток данных становится не отслеживаемым и его очень трудно отлаживать.

Если часть данных должна совместно использоваться несколькими компонентами, предпочтитеглобальные магазины или жеVuex.

[Ссылка на обсуждение]

А также:

.once а также.sync устарели. Реквизит теперь всегда в одну сторону. Для создания побочных эффектов в родительской области компонент должен явноemit событие вместо того, чтобы полагаться на неявное связывание.

(Итак, онпредложить это использовать$emit а также$on)

Я волнуюсь из-за:

каждыйstore а такжеevent имеет глобальную видимость (поправьте меня, если я ошибаюсь);Это очень много, чтобы создать новый магазин для каждого незначительного общения;

Что я хочу этообъем как-тоevents или жеstores видимость для компонентов братьев и сестер. Или, возможно, я не уловил идею.

Итак, как правильно общаться?

 Tremendus Apps27 июл. 2016 г., 20:23
Поэтому я рассмотрел ту же проблему. Мое решение состоит в том, чтобы использовать передатчик событий с широковещательным каналом, который эквивалентен «области действия» - то есть для установления связи между дочерним / родительским устройством и родным братом используется один и тот же канал. В моем случае я использую радио библиотекуradio.uxder.com потому что это всего лишь несколько строк кода и его пуленепробиваемый, но многие выберут узел EventEmitter.
 eltonkamami27 июл. 2016 г., 17:00
$emit в сочетании сv-model подражать.sync, я думаю, ты должен пойти по пути Vuex

Ответы на вопрос(5)

Хорошо, мы можем общаться между братьями и сестрами через родителей, используяv-on События.

Parent
 |-List of items //sibling 1 - "List"
 |-Details of selected item //sibling 2 - "Details"

Давайте предположим, что мы хотим обновитьDetails компонент, когда мы нажимаем какой-то элемент вList.

вParent:

Шаблон:

<list v-model="listModel"
      v-on:select-item="setSelectedItem" 
></list> 
<details v-model="selectedModel"></details>

Вот:

v-on:select-item это событие, которое будет вызвано вList компонент (см. ниже);setSelectedItem этоParentметод для обновленияselectedModel;

JS:

//...
data () {
  return {
    listModel: ['a', 'b']
    selectedModel: null
  }
},
methods: {
  setSelectedItem (item) {
    this.selectedModel = item //here we change the Detail's model
  },
}
//...

ВList:

Шаблон:

<ul>
  <li v-for="i in list" 
      :value="i"
      @click="select(i, $event)">
        <span v-text="i"></span>
  </li>
</ul>

JS:

//...
data () {
  return {
    selected: null
  }
},
props: {
  list: {
    type: Array,
    required: true
  }
},
methods: {
  select (item) {
    this.selected = item
    this.$emit('select-item', item) // here we call the event we waiting for in "Parent"
  },
}
//...

Вот:

this.$emit('select-item', item) отправит товар черезselect-item прямо в родителя. И родитель отправит его наDetails Посмотреть
Решение Вопроса

В Vue 2.0 я использую механизм eventHub, как показано вдокументация.

Определите централизованный центр событий.

const eventHub = new Vue() // Single event hub

// Distribute to components using global mixin
Vue.mixin({
    data: function () {
        return {
            eventHub: eventHub
        }
    }
})

Теперь в вашем компоненте вы можете генерировать события с

this.eventHub.$emit('update', data)

И слушать ты делаешь

this.eventHub.$on('update', data => {
// do your thing
})

Обновить Пожалуйста, смотрите ответ по@Alex, который описывает более простое решение.

 Webnet18 мар. 2018 г., 01:44
Гораздо более простым решением является использование описанного @Alex -this.$root.$emit() а такжеthis.$root.$on()
 Vini.g.fer16 окт. 2017 г., 17:25
Просто наперед: следите за глобальными миксинами и старайтесь по возможности избегать их, как показано на этой ссылкеvuejs.org/v2/guide/mixins.html#Global-Mixin они могут повлиять даже на сторонние компоненты.
 kakoni04 февр. 2019 г., 14:56
Спасибо за ценный отзыв @GrayedFox, обновил мой ответ соответственно.
 GrayedFox22 окт. 2018 г., 12:53
Для дальнейшего использования, пожалуйста, не обновляйте свой ответ чужим ответом (даже если вы считаете, что это лучше, и вы ссылаетесь на него). Ссылка на альтернативный ответ, или даже попросить OP принять другой, если вы считаете, что они должны - но копирование их ответа в ваш собственный является плохой формой и не поощряет пользователей отдавать должное, когда это необходимо, поскольку они могут просто поднять только ваш только ответ. Поощряйте их переходить (и, следовательно, повышать голос) к ответу, на который вы ссылаетесь, не включая этот ответ в свой.

Что я обычно делаю, если хочу «взломать» нормальные шаблоны общения в Vue, особенно сейчас, когда.sync не рекомендуется, это создание простого EventEmitter, который обрабатывает связь между компонентами. Из одного из моих последних проектов:

import {EventEmitter} from 'events'

var Transmitter = Object.assign({}, EventEmitter.prototype, { /* ... */ })

С этимTransmitter объект, который вы можете затем сделать, в любом компоненте:

import Transmitter from './Transmitter'

var ComponentOne = Vue.extend({
  methods: {
    transmit: Transmitter.emit('update')
  }
})

И создать «принимающий» компонент:

import Transmitter from './Transmitter'

var ComponentTwo = Vue.extend({
  ready: function () {
    Transmitter.on('update', this.doThingOnUpdate)
  }
})

Опять же, это для действительно конкретных целей. Не основывайте все свое приложение на этом шаблоне, используйте что-то вродеVuex вместо.

 Sergei Panfilov28 июл. 2016 г., 09:45
Я уже пользуюсьvuexно, опять же, я должен создать магазин Vuex для каждого незначительного сообщения?
 Hector Lorenzo11 авг. 2016 г., 00:10
Нет, конечно нет, все зависит от контекста. На самом деле мой ответ уходит от vuex. С другой стороны, я обнаружил, что чем больше вы используете vuex и концепцию объекта центрального состояния, тем меньше я полагаюсь на связь между объектами. Но да, согласитесь, все зависит.
 Victor10 авг. 2016 г., 15:40
На самом деле я не согласен с тем, что нам нужно использовать vuex для каждого незначительного сообщения ...
 Hector Lorenzo28 июл. 2016 г., 12:24
Мне сложно говорить с таким количеством информации, но я бы сказал, что если вы уже используетеvuex да, дерзай. Используй это.

Вы даже можете сделать его короче и использоватькорень Vue экземпляр как глобальный концентратор событий:

Компонент 1:

this.$root.$emit('eventing', data);

Компонент 2:

mounted() {
    this.$root.$on('eventing', data => {
        console.log(data);
    });
}
 mwangaben12 февр. 2019 г., 17:51
Абсолютно гений Алекса!
 Vikash Gupta23 сент. 2018 г., 13:48
Самое простое решение из всех ответов
 schad27 февр. 2018 г., 18:14
Это работает лучше, чем определение концентратора событий добавления и присоединение его к любому потребителю событий.
 Impunkj24 июн. 2019 г., 06:31
Но он срабатывает много раз, а не один раз. если я нажму на время, чем это событие огня более одного раза.
 Malkev21 мая 2019 г., 15:55
Если вам нужна только прямая связь с братьями и сестрами, используйте $ parent вместо $ root
 Webnet18 мар. 2018 г., 01:43
Я большой поклонник этого решения, так как мне действительно не нравятся события, имеющие масштаб. Тем не менее, я не с VueJS каждый день, поэтому мне любопытно, есть ли кто-нибудь, кто видит проблемы с этим подходом.
 nada24 окт. 2018 г., 16:17
красивый, короткий и простой в реализации, легко понять
 Mateo31 окт. 2017 г., 11:02
Это отлично работает, отличное и очень лаконичное решение.

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

Типы связи

Первое, что нужно понять при разработке приложения Vue (или фактически любого приложения на основе компонентов), - это то, что существуют разные типы связи, которые зависят от того, с какими проблемами мы имеем дело, и им нужны свои собственные каналы связи.

Бизнес логика: относится ко всему, что относится к вашему приложению и его цели.

Логика представления: все, с чем взаимодействует пользователь или что является результатом взаимодействия с пользователем.

Эти две проблемы связаны с этими типами общения:

Состояние приложенияРодитель-ребенокРебенок-РодительБратья и сестры

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

Каналы связи

Канал - это свободный термин, который я буду использовать для обозначения конкретных реализаций для обмена данными вокруг приложения Vue.

Реквизит (логика презентации)

Самый простой канал связи в Vue для прямогоРодитель-Ребенок коммуникации. Его в основном следует использовать для передачи данных, связанных с логикой представления или ограниченным набором данных, по иерархии.

Ссылки и методы (логика представления)

Когда нет смысла использовать опору, чтобы позволить ребенку обрабатывать событие от родителя,созданиеref на дочернем компоненте и вызывая его методы это просто хорошо.

Некоторые люди могут сказать, что это тесная связь между родителем и ребенком, но это та же связь, что и при использовании реквизита. Если мы можем договориться о контракте для реквизита, мы можем также договориться о контракте для методов.

События (Логика презентации)

$emit а также$on, Самый простой канал связи для прямого общения между ребенком и родителем. Опять же, следует использовать логику представления.

Шина событий (Оба)

Большинство ответов дают хорошие альтернативы для шины событий, которая является одним из каналов связи, доступных для удаленных компонентов, или чего-то еще.

Это может оказаться полезным при передаче реквизитов повсюду - от дна к глубоко вложенным дочерним компонентам, при этом почти никакие другие компоненты не нуждаются в них между ними.

Будьте осторожны: последующее создание компонентов, привязывающих себя к шине событий, будет связано более одного раза, что приведет к срабатыванию нескольких обработчиков и утечкам. Лично я никогда не чувствовал необходимость в шине событий во всех одностраничных приложениях, которые я разрабатывал в прошлом.

Следующее демонстрирует, как простая ошибка приводит к утечке, когдаItem компонент по-прежнему срабатывает, даже если удален из DOM.

// A component that binds to a custom 'update' event.
var Item = {
  template: `<li>{{text}}</li>`,
  props: {
    text: Number
  },
  mounted() {
    this.$root.$on('update', () => {
      console.log(this.text, 'is still alive');
    });
  },
};

// Component that emits events
var List = new Vue({
  el: '#app',
  components: {
    Item
  },
  data: {
    items: [1, 2, 3, 4]
  },
  updated() {
    this.$root.$emit('update');
  },
  methods: {
    onRemove() {
      console.log('slice');
      this.items = this.items.slice(0, -1);
    }
  }
});
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>

<div id="app">
  <button type="button" @click="onRemove">Remove</button>
  <ul>
    <item v-for="item in items" :key="item" :text="item"></item>
  </ul>
</div>

Не забудьте удалить слушателей вdestroyed жизненный цикл крюка.

Централизованный магазин (Бизнес логика)

Vuex это путь с Vue длягосударственное управление, Он предлагает гораздо больше, чем просто события, и он готов к полномасштабному применению.

И сейчасты спрашиваешь:

[S] я должен создать магазин Vuex для каждого второстепенного сообщения?

Это действительно светит, когда:

иметь дело с вашей бизнес-логикой,общение с бэкэндом

Таким образом, ваши компоненты могут действительно сосредоточиться на том, чем они должны быть, управляя пользовательскими интерфейсами.

Это не значит, что вы не можете использовать ее для компонентной логики, но я бы применил эту логику к модулю Vuex с пространством имен, имеющему только необходимое глобальное состояние пользовательского интерфейса.

Чтобы не иметь дело с большим беспорядком всего в глобальном состоянии, мы должны разделить хранилище на несколько модулей пространства имен.

Типы компонентов

Чтобы организовать все эти коммуникации и упростить повторное использование, мы должны рассматривать компоненты как два разных типа.

Контейнеры для приложенийОбщие компоненты

Опять же, это не означает, что универсальный компонент должен быть повторно использован или что контейнер конкретного приложения не может быть повторно использован, но у них разные обязанности.

Контейнеры для приложений

Это просто простой компонент Vue, который оборачивает другие компоненты Vue (общие или другие специфичные для приложения контейнеры). Именно здесь должно происходить взаимодействие хранилища Vuex, и этот контейнер должен взаимодействовать с помощью других более простых средств, таких как реквизиты и прослушиватели событий.

Эти контейнеры могут даже не иметь нативных элементов DOM вообще, и общие компоненты справляются с этим.

объем как-тоevents или жеstores видимость для компонентов братьев и сестер

Вот где происходит обзор. Большинство компонентов не знают о хранилище, и этот компонент должен (в основном) использовать один модуль хранилища с пространством имен с ограниченным наборомgetters а такжеactions применяется с прилагаемыми картографами Vuex.

Общие компоненты

Они должны получать свои данные из реквизита, вносить изменения в свои локальные данные и генерировать простые события. Большую часть времени они не должны знать, что магазин Vuex существует.

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

Брат и сестра

Итак, после всего этого, как мы должны общаться между двумя родственными компонентами?

Это проще понять на примере: скажем, у нас есть поле ввода, и его данные должны быть общими для всего приложения (братья и сестры в разных местах дерева) и сохраняться с помощью бэкэнда.

Начиная схудший вариантнаш компонент будет смешиватьсяпрезентация а такжебизнес логика.

// MyInput.vue
<template>
    <div class="my-input">
        <label>Data</label>
        <input type="text"
            :value="value" 
            :input="onChange($event.target.value)">
    </div>
</template>
<script>
    import axios from 'axios';

    export default {
        data() {
            return {
                value: "",
            };
        },
        mounted() {
            this.$root.$on('sync', data => {
                this.value = data.myServerValue;
            });
        },
        methods: {
            onChange(value) {
                this.value = value;
                axios.post('http://example.com/api/update', {
                        myServerValue: value
                    })
                    .then((response) => {
                        this.$root.$emit('update', response.data);
                    });
            }
        }
    }
</script>

Чтобы разделить эти две проблемы, мы должны поместить наш компонент в специальный контейнер приложения и сохранить логику представления в нашем общем компоненте ввода.

Наш входной компонент теперь можно использовать повторно, и он не знает ни о бэкенде, ни о братьях и сестрах.

// MyInput.vue
// the template is the same as above
<script>
    export default {
        props: {
            initial: {
                type: String,
                default: ""
            }
        },
        data() {
            return {
                value: this.initial,
            };
        },
        methods: {
            onChange(value) {
                this.value = value;
                this.$emit('change', value);
            }
        }
    }
</script>

Контейнер для нашего приложения теперь может стать мостом между бизнес-логикой и презентацией.

// MyAppCard.vue
<template>
    <div class="container">
        <card-body>
            <my-input :initial="serverValue" @change="updateState"></my-input>
            <my-input :initial="otherValue" @change="updateState"></my-input>

        </card-body>
        <card-footer>
            <my-button :disabled="!serverValue || !otherValue"
                       @click="saveState"></my-button>
        </card-footer>
    </div>
</template>
<script>
    import { mapGetters, mapActions } from 'vuex';
    import { NS, ACTIONS, GETTERS } from '@/store/modules/api';
    import { MyButton, MyInput } from './components';

    export default {
        components: {
            MyInput,
            MyButton,
        },
        computed: mapGetters(NS, [
            GETTERS.serverValue,
            GETTERS.otherValue,
        ]),
        methods: mapActions(NS, [
            ACTIONS.updateState,
            ACTIONS.updateState,
        ])
    }
</script>

Так как магазин Vuexдействия разобраться с бэкэнд-коммуникацией, нашему контейнеру здесь не нужно знать об axios и бэкэнде.

 Emile Bergeron03 окт. 2018 г., 15:58
@LordZed Это не то, что я сказал.
 vandroid21 сент. 2018 г., 21:52
Мне нравится этот ответ. Но не могли бы вы рассказать о Event Bus и записке «Будьте осторожны:»? Может быть, вы можете привести какой-то пример, я не понимаю, как компоненты могут быть связаны дважды.
 Lord Zed27 сент. 2018 г., 14:44
Как вы общаетесь между родительским компонентом и большим дочерним компонентом, например, при проверке формы. Где родительский компонент - это страница, потомок - это форма, а великий потомок - элемент формы ввода?
 ghybs18 мая 2018 г., 15:06
Согласен с комментарием о методах бытията же связь с использованием реквизита"
 Emile Bergeron03 окт. 2018 г., 06:08
@LordZed Это действительно зависит, но из моего понимания вашей ситуации это выглядит как проблема дизайна. Vue следует использовать в основном для логики представления. Проверка формы должна выполняться в другом месте, как в интерфейсном интерфейсе JS API, который вызывается действием Vuex с данными из формы.
 Lord Zed03 окт. 2018 г., 10:27
@EmileBergeron Вы хотите сказать, что теперь внезапно нам не нужна проверка входных данных на стороне клиента?
 Emile Bergeron03 окт. 2018 г., 06:06
@vandroid Я создал простой пример, который показывает утечку, когда слушатели не удаляются должным образом, как и все примеры в этой теме.

Ваш ответ на вопрос