code

VueJs 2.0의 형제 구성 요소 간 통신

codestyles 2020. 9. 13. 10:34
반응형

VueJs 2.0의 형제 구성 요소 간 통신


vuejs에서 2.0 model.sync됩니다 되지 않습니다 .

그렇다면 vuejs 2.0 에서 형제 구성 요소간에 통신하는 적절한 방법은 무엇 입니까?

내가 Vue 2.0 에서 아이디어 를 잡았을 때 상점이나 이벤트 버스를 사용하여 형제 통신 을하는 것 입니다.

에반 에 따르면 :

또한 "구성 요소간에 데이터 전달"은 일반적으로 나쁜 생각이라고 언급 할 가치가 있습니다. 결국 데이터 흐름은 추적 할 수없고 디버깅하기가 매우 어렵 기 때문입니다.

여러 구성 요소에서 데이터를 공유 해야하는 경우 글로벌 스토어 또는 Vuex를 선호합니다 .

[ 토론 링크 ]

과:

.once그리고 .sync사용되지 않습니다. 이제 소품은 항상 단방향입니다. 상위 범위에서 부작용을 생성하려면 구성 요소가 emit암시 적 바인딩에 의존하는 대신 명시 적 으로 이벤트 를 생성해야합니다 .

(그래서 그가 제안 사용하는 것입니다 $emit$on)

다음과 같은 이유로 걱정됩니다.

  • storeevent글로벌 가시성을 (내가 틀렸다면 정정 해줘)이있다;
  • 사소한 의사 소통을 할 때마다 새로운 상점을 만드는 것이 중요합니다.

내가 원하는 것은 형제 구성 요소의 범위events 또는 stores가시성을 어떻게 든 범위를 지정하는 것 입니다. 아니면 아이디어를 이해하지 못했을 수도 있습니다.

그렇다면 올바른 방식으로 의사 소통하는 방법은 무엇입니까?


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 의 답변을 참조하십시오 .


더 짧게 만들고 루트 Vue 인스턴스를 전역 Event Hub로 사용할 수도 있습니다 .

성분 1 :

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

성분 2 :

mounted() {
    this.$root.$on('eventing', data => {
        console.log(data);
    });
}

나는 이것이 오래된 질문이라는 것을 알고 있지만 다른 커뮤니케이션 채널과 더 높은 관점에서 앱과 커뮤니케이션을 보는 방법을 공개하고 싶었습니다.


커뮤니케이션 유형

Vue 응용 프로그램 (또는 실제로 모든 구성 요소 기반 응용 프로그램)을 디자인 할 때 가장 먼저 이해해야 할 것은 우리가 다루는 문제에 따라 다른 통신 유형이 있고 자체 통신 채널이 필요하다는 것입니다.

비즈니스 로직 : 앱과 그 목표와 관련된 모든 것을 나타냅니다.

프리젠 테이션 로직 : 사용자가 상호 작용하거나 사용자의 상호 작용으로 인해 발생하는 모든 것.

이 두 가지 문제는 다음 유형의 커뮤니케이션과 관련이 있습니다.

  • 신청 상태
  • 부모-자식
  • 자녀-부모
  • 형제 자매

각 유형은 올바른 통신 채널을 사용해야합니다.


커뮤니케이션 채널

채널은 Vue 앱 주변에서 데이터를 교환하기 위해 구체적인 구현을 참조하는 데 사용할 느슨한 용어입니다.

Props (프레젠테이션 로직)

직접 부모-자녀 커뮤니케이션을 위한 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/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를 사용하는 방법 입니다. 이벤트 이상을 제공하며 본격적인 적용을위한 준비가되어 있습니다.

이제 묻습니다 .

사소한 커뮤니케이션마다 vuex 스토어를 만들어야하나요?

다음과 같은 경우에 정말 빛납니다.

  • 비즈니스 로직을 다루고,
  • 백엔드와 통신

따라서 구성 요소는 사용자 인터페이스를 관리하는 원래 의도에 집중할 수 있습니다.

구성 요소 논리에 사용할 수 없다는 의미는 아니지만 필요한 전역 UI 상태 만있는 네임 스페이스가 지정된 Vuex 모듈로 해당 논리의 범위를 지정합니다.

전역 상태에서 모든 것이 엉망이되는 것을 방지하려면 여러 네임 스페이스 모듈로 저장소를 분할해야합니다.


구성 요소 유형

이러한 모든 통신을 조율하고 재사용을 용이하게하려면 구성 요소를 두 가지 유형으로 생각해야합니다.

  • 앱별 컨테이너
  • 일반 구성 요소

다시 말하지만, 일반 구성 요소를 재사용해야하거나 앱 특정 컨테이너를 재사용 할 수 없다는 의미는 아니지만 책임이 다릅니다.

앱별 컨테이너

이들은 다른 Vue 구성 요소 (일반 또는 기타 앱 특정 컨테이너)를 래핑하는 단순한 Vue 구성 요소입니다. 여기서 Vuex 스토어 통신이 발생하고이 컨테이너는 소품 및 이벤트 리스너와 같은 다른 간단한 수단을 통해 통신해야합니다.

이러한 컨테이너에는 네이티브 DOM 요소가 전혀 없을 수도 있으며 일반 구성 요소가이를 처리 할 수 ​​있습니다.

범위를 어떻게 든 events또는 stores형제 자매 구성 요소에 대한 가시성

여기에서 범위 지정이 발생합니다. 대부분의 구성 요소는 상점에 대해 알고하지 않으며,이 구성 요소는 (대부분) 사용 하나의 제한된으로 저장 모듈을 네임 스페이스해야 getters하고 actions제공된 Vuex 매퍼 적용.

일반 구성 요소

이들은 소품에서 데이터를 수신하고, 자체 로컬 데이터를 변경하고, 간단한 이벤트를 생성해야합니다. 대부분의 경우 Vuex 스토어가 존재하는지 알면 안됩니다.

다른 UI 구성 요소에 대한 유일한 책임이있을 수 있으므로 컨테이너라고도합니다.


형제 커뮤니케이션

그렇다면이 모든 과정에서 두 형제 구성 요소간에 어떻게 통신해야합니까?

예를 들어 이해하기가 더 쉽습니다. 입력 상자가 있고 그 데이터가 앱 전체 (트리의 다른 위치에있는 형제)에서 공유되고 백엔드와 유지되어야한다고 가정합니다.

최악의 시나리오 부터 시작하여 구성 요소는 프레젠테이션비즈니스 로직을 혼합 합니다 .

// 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 및 백엔드에 대해 알 필요가 없습니다.


좋아요, 우리는 v-on이벤트를 사용하여 부모를 통해 형제들간에 통신 할 수 있습니다.

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

Let's assume that we want update Details component when we click some element in List.


in Parent:

Template:

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

Here:

  • v-on:select-item it's an event, that will be called in List component (see below);
  • setSelectedItem it's a Parent's method to update selectedModel;

JS:

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

In List:

Template:

<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"
  },
}
//...

Here:

  • this.$emit('select-item', item) will send item via select-item directly in parent. And parent will send it to the Details view

What I usually do if I want to "hack" the normal patterns of communication in Vue, specially now that .sync is deprecated, is to create a simple EventEmitter that handles communication between components. From one of my latest projects:

import {EventEmitter} from 'events'

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

With this Transmitter object you can then do, in any component:

import Transmitter from './Transmitter'

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

And to create a "receiving" component:

import Transmitter from './Transmitter'

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

Again, this is for really specific uses. Don't base your whole application on this pattern, use something like Vuex instead.

참고URL : https://stackoverflow.com/questions/38616167/communication-between-sibling-components-in-vuejs-2-0

반응형