vue.js - 컴포넌트 - Events, slot

2 minute read

사용자 정의 이벤트

  • Vue에서는 자식 컴포넌트의 문제가 발생했을 때, 부모 컴포넌트로 알리는 방법은 사용자 정의 이벤트로 알릴 수 있습니다.
  • 모든 Vue인스턴스는 $on(eventName)을 사용하여 이벤트를 감지하거나 $emit(eventName)을 사용하여 이벤트를 트리거합니다.
  • 하지만 Vue의 $on과 $emit이 비슷하게 동작하지만 브라우저의 EventTarget API인 addEventListener, dispatchEvent와 별개입니다.

v-on을 이용한 사용자 지정 이벤트

  • 부모 컴포넌트는 자식 컴포넌트가 사용되는 템플릿에 직접 v-on을 사용하여 자식 컴포넌트에서 보내진 이벤트를 들을 수 있습니다.
  • v-on과 다르게 $on은 자식에서 호출한 이벤트를 감지하지 않습니다. 반드시 v-on을 템플릿에서 지정해야 감지할 수 있습니다.
<div id="counter-event-example">
  <p></p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
<script>
Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter"></button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})

new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
</script>
  • 컴포넌트에서 네이티브 이벤트를 수신할 경우가 있을 수 있습니다. 이러한 경우 v-on에 .native 수식어를 이용하면 사용할 수 있습니다.
<my-component v-on:click.native="doTheThing"></my-component>

비 부모-자식 간의 통신

  • 컴포넌트의 통신이 꼭 부모-자식 관계일 수는 없습니다.
  • 이때 간단하게는 같이 통신할 컴포넌트가 사용할 공통 Vue인스턴스르르 만들어서 이벤트 버스를 사용할 수 있습니다.
var bus = new Vue()

// 컴포넌트 A의 메소드
bus.$emit('id-selected', 1)

// 컴포넌트 B의 created 훅
bus.$on('id-selected', function (id) {
  // ...
})
  • 더 복잡한 경우에는 여기를 참고하세요. 상태 관리

컴포넌트 범위

<child-component>
  
</child-component>
  • 위와 같은 템플릿이 있는 경우 message는 자식 데이터가 아닌 부모 데이터에 바인딩됩니다.
  • 상위 템플릿의 모든 내용은 상위 범위로 컴파일됩니다. 하위 템플릿의 모든 내용은 하위 범위에서 컴파일됩니다.

단일 슬롯

  • 하위 컴포넌트에 최소한 하나의 slot이 없으면 부모 컨텐츠가 삭제됩니다.
  • slot에 삽입할 컨텐츠가 없는 경우 대체 컨텐츠로 표현됩니다.
<div>
  <h1>나는 부모 컴포넌트의 제목입니다</h1>
  <my-component>
    <p>이것은 원본 컨텐츠 입니다.</p>
    <p>이것은 원본 중 추가 컨텐츠 입니다</p>
  </my-component>
</div>
<script>
Vue.component('my-component', {
  template: `
    <div>
      <h2>나는 자식 컴포넌트의 제목입니다</h2>
      <slot>
        제공된 컨텐츠가 없는 경우에만 보실 수 있습니다.
      </slot>
    </div>
    `
})
</script>
<!-- 위 예제의 렌더링 결과입니다. -->
<div>
  <h1>나는 부모 컴포넌트의 제목입니다</h1>
  <div>
    <h2>나는 자식 컴포넌트의 제목 입니다</h2>
    <p>이것은 원본 컨텐츠 입니다.</p>
    <p>이것은 원본 중 추가 컨텐츠 입니다</p>
  </div>
</div>

동적 컴포넌트

  • 같은 마운트 포인트를 사용하고 예약된 <component> 엘리먼트를 사용하여 여러 컴포넌트 간에 동적으로 트랜지션하고 is 속성에 동적으로 바인드 할 수 있습니다.
<component v-bind:is="currentView">
  <!-- vm.currentView가 변경되면 컴포넌트가 변경됩니다! -->
</component>
<script>
var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})
</script>
  • 동적 컴포넌트에서 keep-alive을 이용해 트랜지션된 컴포넌트를 메모리에 유지하거나, 다시 렌더링되지 않게 할 수 있습니다. 자세한 내용은 keep-alive API문서를 확인하시면 됩니다.

ps. 찾아보고 공부하면서 정리하는 내용이라 틀릴 수 있음을 알려드립니다.

참고