[javascript] VueJs 2.0은 손자에서 손자 부모 구성 요소로 이벤트를 방출합니다.

Vue.js 2.0은 손자에서 손자 부모 구성 요소로 이벤트를 내 보내지 않는 것 같습니다.

Vue.component('parent', {
  template: '<div>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
  data(){
    return {
      action: 'No action'
    }
  },
  methods: {
    performAction() { this.action = 'actionDone' }
  }
})

Vue.component('child', {
  template: '<div>I am the child <grand-child></grand-child></div>'
})

Vue.component('grand-child', {
  template: '<div>I am the grand-child <button @click="doEvent">Do Event</button></div>',
  methods: {
    doEvent() { this.$emit('eventtriggered') }
  }
})

new Vue({
  el: '#app'
})

이 JsFiddle은 https://jsfiddle.net/y5dvkqbd/4/ 문제를 해결 하지만 두 가지 이벤트를 내 보냅니다 .

  • 손자에서 중간 구성 요소까지 하나
  • 그런 다음 중간 구성 요소에서 그랜드 부모로 다시 방출

이 중간 이벤트를 추가하는 것은 반복적이고 불필요 해 보입니다. 내가 알지 못하는 조부모에게 직접 방출하는 방법이 있습니까?



답변

Vue 2.4는 다음을 사용하여 계층 구조 위로 이벤트를 쉽게 전달하는 방법을 도입했습니다. vm.$listeners

에서 https://vuejs.org/v2/api/#vm-listeners :

상위 범위 v-on이벤트 리스너를 포함합니다 ( .native수정 자 없음). 이것은 v-on="$listeners"투명한 래퍼 구성 요소를 만들 때 유용합니다.

템플릿 v-on="$listeners"grand-child구성 요소에서 사용 하는 아래 스 니펫을 참조하세요 child.

Vue.component('parent', {
  template:
    '<div>' +
      '<p>I am the parent. The value is {{displayValue}}.</p>' +
      '<child @toggle-value="toggleValue"></child>' +
    '</div>',
  data() {
    return {
      value: false
    }
  },
  methods: {
    toggleValue() { this.value = !this.value }
  },
  computed: {
    displayValue() {
      return (this.value ? "ON" : "OFF")
    }
  }
})

Vue.component('child', {
  template:
    '<div class="child">' +
      '<p>I am the child. I\'m just a wrapper providing some UI.</p>' +
      '<grand-child v-on="$listeners"></grand-child>' +
    '</div>'
})

Vue.component('grand-child', {
  template:
    '<div class="child">' +
      '<p>I am the grand-child: ' +
        '<button @click="emitToggleEvent">Toggle the value</button>' +
      '</p>' +
    '</div>',
  methods: {
    emitToggleEvent() { this.$emit('toggle-value') }
  }
})

new Vue({
  el: '#app'
})
.child {
  padding: 10px;
  border: 1px solid #ddd;
  background: #f0f0f0
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <parent></parent>
</div>


답변

Vue 커뮤니티는 일반적으로 이러한 종류의 문제를 해결하기 위해 Vuex를 사용하는 것을 선호합니다. Vuex 상태가 변경되고 DOM 표현이 그 상태에서 흐르기 때문에 많은 경우 이벤트가 필요하지 않습니다.

이를 제외하고 다시 방출하는 것이 차선책 이 될 수 있으며 마지막 으로이 질문에 대한 다른 투표율이 높은 답변에 자세히 설명 된대로 이벤트 버스를 사용할 수 있습니다.

아래 답변은이 질문에 대한 저의 원래 답변이며 Vue에 대해 더 많은 경험을 쌓은 지금 제가 취할 접근법이 아닙니다.


이것은 Vue의 디자인 선택에 동의하지 않고 DOM에 의존 할 수있는 경우입니다.

에서 grand-child,

methods: {
    doEvent() {
        try {
            this.$el.dispatchEvent(new Event("eventtriggered"));
        } catch (e) {
            // handle IE not supporting Event constructor
            var evt = document.createEvent("Event");
            evt.initEvent("eventtriggered", true, false);
            this.$el.dispatchEvent(evt);
        }
    }
}

과에서 parent,

mounted(){
    this.$el.addEventListener("eventtriggered", () => this.performAction())
}

그렇지 않으면 예, 다시 내보내거나 버스를 사용해야합니다.

참고 : IE를 처리하기 위해 doEvent 메서드에 코드를 추가했습니다. 그 코드는 재사용 가능한 방식으로 추출 될 수 있습니다.


답변

NEW ANSWER (2018 년 11 월 업데이트)

저는 우리가 $parent그랜드 하위 구성 요소 의 속성 을 활용하여 실제로이 작업을 수행 할 수 있음을 발견했습니다 .

this.$parent.$emit("submit", {somekey: somevalue})

훨씬 더 깨끗하고 간단합니다.


답변

네, 맞습니다 이벤트는 아이에서 부모로만 이동합니다. 그들은 예를 들어 아이에서 조부모로 더 나아 가지 않습니다.

Vue 문서 (간략히)는 비 부모-자녀 통신 섹션 에서이 상황을 다룹니다.

일반적인 아이디어는 조부모 구성 요소 Vue에서 소품을 통해 조부모에서 자식 및 손자에게 전달 되는 빈 구성 요소를 만드는 것입니다. 그런 다음 조부모는 이벤트를 듣고 손자는 해당 “이벤트 버스”에서 이벤트를 내 보냅니다.

일부 애플리케이션은 구성 요소 별 이벤트 버스 대신 글로벌 이벤트 버스를 사용합니다. 전역 이벤트 버스를 사용한다는 것은 이벤트가 서로 다른 구성 요소간에 충돌하지 않도록 고유 한 이벤트 이름이나 네임 스페이스가 필요하다는 것을 의미합니다.

다음은 간단한 전역 이벤트 버스를 구현하는 방법 의 예입니다 .


답변

또 다른 솔루션은 루트 노드 에서 on / emit됩니다 .

용도 vm.$root.$emit그랜드 아이 , 다음 사용 vm.$root.$on조상에서 (또는 어디서나 싶습니다).

업데이트 됨 : 특정 상황에서 리스너를 비활성화하고 싶을 때가 있습니다 . vm. $ off를 사용 합니다 (예 : vm.$root.off('event-name')inside lifecycle hook = beforeDestroy ).

Vue.component('parent', {
  template: '<div><button @click="toggleEventListener()">Listener is {{eventEnable ? "On" : "Off"}}</button>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
  data(){
    return {
      action: 1,
      eventEnable: false
    }
  },
  created: function () {
    this.addEventListener()
  },
  beforeDestroy: function () {
    this.removeEventListener()
  },
  methods: {
    performAction() { this.action += 1 },
    toggleEventListener: function () {
      if (this.eventEnable) {
        this.removeEventListener()
      } else {
        this.addEventListener()
      }
    },
    addEventListener: function () {
      this.$root.$on('eventtriggered1', () => {
        this.performAction()
      })
      this.eventEnable = true
    },
    removeEventListener: function () {
      this.$root.$off('eventtriggered1')
      this.eventEnable = false
    }
  }
})

Vue.component('child', {
  template: '<div>I am the child <grand-child @eventtriggered="doEvent"></grand-child></div>',
  methods: {
    doEvent() {
    	//this.$emit('eventtriggered') 
    }
  }
})

Vue.component('grand-child', {
  template: '<div>I am the grand-child <button @click="doEvent">Emit Event</button></div>',
  methods: {
    doEvent() { this.$root.$emit('eventtriggered1') }
  }
})

new Vue({
  el: '#app'
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
  <parent></parent>
</div>


답변

유연하고 단순히 모든 부모와 부모에게 루트까지 재귀 적으로 이벤트를 브로드 캐스트하려면 다음과 같이 할 수 있습니다.

let vm = this.$parent

while(vm) {
    vm.$emit('submit')
    vm = vm.$parent
}


답변

이벤트 버스를 이용하는 경우는 이것뿐입니다 !! 깊은 중첩 자식에서 직접 부모가 아닌 통신으로 데이터를 전달합니다.

첫 번째 : 다음 내용으로 js 파일 (이름 : eventbus.js)을 만듭니다.

import Vue from 'vue'
Vue.prototype.$event = new Vue()

둘째 : 하위 구성 요소에서 이벤트를 생성합니다.

this.$event.$emit('event_name', 'data to pass')

셋째 : 부모에서 해당 이벤트를 수신합니다.

this.$event.$on('event_name', (data) => {
  console.log(data)
})

참고 : 해당 이벤트를 더 이상 원하지 않는 경우 등록을 취소하십시오.

this.$event.$off('event_name')

정보 : 아래 개인 의견을 읽을 필요가 없습니다.

나는 손자와 조부모의 의사 소통 (또는 유사한 의사 소통 수준)에 vuex를 사용하고 싶지 않습니다.

vue.js에서 조부모에서 손자에게 데이터를 전달하기 위해 provide / inject를 사용할 수 있습니다 . 그러나 반대되는 것과 비슷한 것이 없습니다. (손자부터 조부모까지) 그래서 그런 소통이 필요할 때마다 이벤트 버스를 이용합니다.