Вопрос:

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

javascript vue.js vuejs2 vue-component vuex

18855 просмотра

5 ответа

5822 Репутация автора

В vuejs 2.0 model.syncбудет устаревшим .

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

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

По словам Эвана :

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

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

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

А также:

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

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

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

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

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

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

Автор: Sergei Panfilov Источник Размещён: 27.07.2016 02:40

Ответы (5)


5 плюса

798 Репутация автора

Что я обычно делаю, если хочу «взломать» нормальные шаблоны взаимодействия в 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этого.

Автор: Hector Lorenzo Размещён: 28.07.2016 03:47

9 плюса

5822 Репутация автора

Хорошо, мы можем общаться между братьями и сестрами через родителя, используя 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просмотр
Автор: Sergei Panfilov Размещён: 10.08.2016 02:29

58 плюса

3722 Репутация автора

Решение

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

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

    const eventHub = new Vue() // Single event hub
    
    // Distribute to components using global mixin
    Vue.mixin({
        data: function () {
            return {
                eventHub: eventHub
            }
        }
    })
    
  2. Теперь в вашем компоненте вы можете генерировать события с

    this.eventHub.$emit('update', data)
    
  3. И слушать ты делаешь

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

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

Автор: kakoni Размещён: 05.11.2016 08:41

55 плюса

11442 Репутация автора

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

Компонент 1:

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

Компонент 2:

mounted() {
    this.$root.$on('eventing', data => {
        console.log(data);
    });
}
Автор: Alex Размещён: 29.10.2017 07:14

26 плюса

10567 Репутация автора

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


Типы связи

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

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

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

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

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

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


Каналы связи

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

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

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

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

Если нет смысла использовать реквизит для того, чтобы дочерний элемент мог обрабатывать событие от родителя, настройка a 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/vue@2.5.17/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 действия иметь дело с серверной связи, наш контейнер здесь не нужно знать о Вардар и внутреннем интерфейсе.

Автор: Emile Bergeron Размещён: 07.04.2018 01:34
Вопросы из категории :
32x32