[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를 사용할 수 있습니다 . 그러나 반대되는 것과 비슷한 것이 없습니다. (손자부터 조부모까지) 그래서 그런 소통이 필요할 때마다 이벤트 버스를 이용합니다.