programing

VueJs 2.0은 손녀에서 손녀 컴포넌트로 이벤트를 내보냅니다.

lovecodes 2022. 7. 28. 23:47
반응형

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/ 문제를 해결하지만 다음 두 가지 이벤트를 처리합니다.

  • 손녀부터 중간 컴포넌트까지 1개
  • 그리고 나서 중간 구성 요소에서 조부모에게 다시 방출합니다.

이 중간 이벤트를 추가하는 것은 반복적이고 불필요해 보입니다.제가 모르는 조부모님께 직접 배출할 수 있는 방법이 있나요?

2.는 Vue 2.4를 사용하여 했습니다.에서는 이벤트를 계층으로 쉽게 전달할 수 있는 방법이 도입되었습니다.vm.$listeners

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

" " 가 됩니다.v-on 리스너없음').native이치노 구성 수 .v-on="$listeners" 구성 요소를 만들 때 - 투명 래퍼 구성 요소를 만들 때 합니다.

하다를 사용해서 아래 을 보세요.v-on="$listeners" grand-childchild★★★★★★★★★★★★★★★★★★:

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>

새로운 답변 (2018년 11월 갱신)

할 수 되었습니다.$parent「 」의 「 」의 「 」

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

훨씬 더 깔끔하고 심플합니다.

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

그것을 제외하고, 재발신하는 것이 차선책이 될 가능성이 있습니다.마지막으로 이 질문에 대한 다른 투표율이 높은 답변에 기재되어 있는 이벤트버스를 사용할 수도 있습니다.

아래 답변은 이 질문에 대한 저의 원래 답변이며, 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 메서드에 코드를 추가했습니다.그 코드는 재사용 가능한 방법으로 추출할 수 있습니다.

네, 맞아요. 한 아이에서 한 부모로 넘어갈 뿐이죠.예를 들어 자녀에서 조부모로 넘어가지 않습니다.

Vue 매뉴얼(간단히)에서는 "비부모-자녀 통신" 섹션에서 이 상황에 대해 설명합니다.

, , , , 할아버지, 할아버지, 할아버지, 할아버지, 할아버지, 할아버지, 할아버지, 할아버지, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, , 할머니, 할머니, , 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니, 할머니,Vue소품을 통해 조부모로부터 자녀와 손자에게 전해지는 구성 요소.그러면 조부모는 이벤트를 듣고 손자들은 "이벤트 버스"를 통해 이벤트를 내보냅니다.

일부 응용 프로그램에서는 컴포넌트별 이벤트버스 대신 글로벌이벤트 버스를 사용합니다.글로벌 이벤트버스를 사용하면 이벤트가 다른 컴포넌트 간에 충돌하지 않도록 고유한 이벤트 이름 또는 네임스페이스를 사용해야 합니다.

다음으로 간단한 글로벌이벤트 버스를 구현하는 예를 나타냅니다.

만약 여러분이 융통성 있게 모든 부모와 그들의 부모에게 이벤트를 뿌리까지 재귀적으로 방송하고 싶다면, 여러분은 다음과 같은 것을 할 수 있습니다.

let vm = this.$parent

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

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

vm.$root.$emit손자손녀의 경우, 그 후 사용vm.$root.$on조상(또는 당신이 원하는 곳)에서요.

업데이트됨: 특정 상황에서 수신기를 사용하지 않도록 설정할 수 있습니다. VM을 사용하십시오.$off (예:vm.$root.off('event-name')내부 라이프 사이클 훅=파괴 전).

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>

이벤트 버스를 이용하는 경우는 이것뿐입니다!!딥 네스트된 자식으로부터 직접 부모가 아닌 부모에게 데이터를 전달하기 위한 통신입니다.

번째: 다음 내용으로 js 파일(이벤트버스.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에서는 조부모에서 손자녀에게 데이터를 전달하기 위해 제공/인젝트를 사용할 수 있습니다.하지만 그 반대는 없습니다. 그래서 저는 그런 소통이 필요할 때마다 이벤트 버스를 이용합니다.

@digout 답변을 바탕으로 짧은 믹스인을 만들었습니다.Vue 인스턴스 초기화(새로운 Vue...) 전에 프로젝트에서 글로벌하게 사용하기 위해 배치하려고 합니다.일반 이벤트와 유사하게 사용할 수 있습니다.

Vue.mixin({
  methods: {
    $propagatedEmit: function (event, payload) {
      let vm = this.$parent;
      while (vm) {
        vm.$emit(event, payload);
        vm = vm.$parent;
      }
    }
  }
})

에는 VueJ2가 .$parent이치노

인 컴포넌트도 되어 있습니다.$parent★★★★★★★★★★★★★★★★★★.

그런 다음 "부모" 구성 요소에 액세스하려면 "부모" 구성 요소에 액세스해야 합니다.

this.$parent["$parent"].$emit("myevent", { data: 123 });

어쨌든 이것은 좀 까다롭습니다.다른 응답자들이 말한 것처럼 Vuex와 같은 글로벌 스테이트 매니저나 이와 유사한 툴을 사용하는 것을 추천합니다.

@kubaklam과 @digout의 답변을 생략하고, 손자녀와 (아마도 먼) 조부모 사이의 모든 부모 컴포넌트에 대해 방출되는 것을 피하기 위해 사용하는 것은 다음과 같습니다.

{
  methods: {
    tunnelEmit (event, ...payload) {
      let vm = this
      while (vm && !vm.$listeners[event]) {
        vm = vm.$parent
      }
      if (!vm) return console.error(`no target listener for event "${event}"`)
      vm.$emit(event, ...payload)
    }
  }
}

많은/모든 컴포넌트를 스토어에 묶고 싶지 않지만 루트 컴포넌트가 스토어/소스로 기능하기를 원하는 먼 손자녀와 함께 컴포넌트를 구축하면 이 컴포넌트가 매우 효과적입니다.이것은 Ember의 철학에 대한 데이터 다운 조치와 유사합니다.단점은 만약 당신이 그 이벤트를 모든 부모에게 듣고 싶다면, 이것은 효과가 없을 것이라는 것입니다.그러나 위의 답변과 같이 $propogateEmit를 @kubaklam에서 사용할 수 있습니다.

편집: 구성 요소의 상위 항목이 아닌 구성 요소로 초기 VM을 설정해야 합니다.예.let vm = this가 아니라let vm = this.$parent

윈도우에 바인딩된 클래스를 만들고 브로드캐스트/리슨 설정을 간소화하여 Vue 앱 내 어디에서나 작업할 수 있도록 함으로써 이 문제를 해결하는 방법이 정말 마음에 듭니다.

window.Event = new class {

    constructor() {
        this.vue = new Vue();
    }

    fire(event, data = null) {
        this.vue.$emit(event, data);
    }

    listen() {
        this.vue.$on(event, callback);  
    }

}

이제 전화만 하면 어디서든 발사/방송/무엇이든 할 수 있습니다.

Event.fire('do-the-thing');

...부모님, 조부모님 등 원하는 모든 것을 전화로 들을 수 있습니다.

Event.listen('do-the-thing', () => {
    alert('Doing the thing!');
});

Vue 3에서는 루트 이벤트에 다음과 같은 몇 가지 근본적인 변화가 발생했습니다.

$on,$off그리고.$once루트 메서드는 더 이상 존재하지 않습니다.이렇게 하면 루트 이벤트를 청취할 수 있기 때문에 이를 대체할 수 있는 것이 어느 정도 있습니다.

createApp(App, {
  // Listen for the 'expand' event
  onExpand() {
    console.log('expand')
  }
})

또 다른 해결책으로는 이벤트 버스를 들 수 있지만 Vue.js 문서에서는 장기적으로 유지보수의 문제를 일으킬 수 있습니다.방출 및 이벤트 싱크가 지속적으로 확산될 수 있으며, 이러한 방출 및 이벤트 싱크의 관리 방법이나 다른 곳에서 영향을 받을 수 있는 컴포넌트에 대한 명확한 중앙 지식이 없습니다.그럼에도 불구하고, 이벤트 버스의 문서들에 의해 제시된 예로는 미트와 소형 이미터있습니다.

단, 문서에서는 다음 순서로 이러한 상황을 처리할 것을 권장합니다.

  • 소품 부모/자녀 커뮤니케이션을 위한 편리한 솔루션.
  • 제공/주입 조상들이 후손들과 소통할 수 있는 간단한 방법입니다(비록 결정적으로 그 반대는 아닙니다).
  • Vuex 글로벌 상태를 명확하게 처리하는 방법.Vuex는 주로 상태를 처리하기 위해 구축되었으며 이벤트 또는 커뮤니케이션만을 위한 것이 아닙니다.

기본적으로 OP의 선택은 이벤트 버스 또는 Vuex를 사용하는 것입니다.이벤트 버스를 일원화하기 위해 Vuex 내에 배치할 수 있습니다(상태도 글로벌하게 이용할 필요가 있는 경우).그렇지 않으면 이벤트 버스의 동작과 위치를 엄격하게 중앙 집중식으로 제어하는 이벤트 버스를 사용하는 것이 도움이 될 수 있습니다.

@digout 응답 삭제.먼 옛날 사람에게 데이터를 보내는 것이 목적이라면 $emit은 전혀 필요 없다고 생각합니다.난 내 엣지 케이스를 위해 이걸 했는데 효과가 있는 것 같아.네, 믹스인을 통해 구현할 수 있지만 반드시 구현할 필요는 없습니다.

/**
 * Send some content as a "message" to a named ancestor of the component calling this method.
 * This is an edge-case method where you need to send a message many levels above the calling component.
 * Your target component must have a receiveFromDescendant(content) method and it decides what
 * to do with the content it gets.
 * @param {string} name - the name of the Vue component eg name: 'myComponentName'
 * @param {object} content - the message content
 */
messageNamedAncestor: function (name, content) {
  let vm = this.$parent
  let found = false
  while (vm && !found) {
    if (vm.$vnode.tag.indexOf('-' + name) > -1) {
      if (vm.receiveFromDescendant) {
        found = true
        vm.receiveFromDescendant(content)
      } else {
        throw new Error(`Found the target component named ${name} but you dont have a receiveFromDescendant method there.`)
      }
    } else {
      vm = vm.$parent
    }
  }
}

상위 항목 지정:

export default {
  name: 'myGreatAncestor',
  ...
  methods: {
     receiveFromDescendant (content) {
        console.log(content)
     }
   }
}

증손자가 말한다

// Tell the ancestor component something important
this.messageNamedAncestor('myGreatAncestor', {
  importantInformation: 'Hello from your great descendant'
})

언급URL : https://stackoverflow.com/questions/42615445/vuejs-2-0-emit-event-from-grand-child-to-his-grand-parent-component

반응형