본문 바로가기
Web/Vue

Vue.js 개념 총정리(2)_컴포넌트 통신규칙

by 나비와꽃기린 2021. 1. 30.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

1. 컴포넌트 통신 규칙이 필요한 이유

Vue경우 컴포넌트로 화면을 구성하기 때문에 같은 페이지라고 하더라도 데이터를 공유할 없다.

이유는 컴포넌트마다 자체적으로 고유한 유효범위(Scope) 가지기 때문이다.

따라서, 직접적으로는 다른 컴포넌트의 값을 참조할 없기 때문에 프레임워크 자체에서 정의한 컴포넌트 데이터전달 규칙을 따라야한다.

 

상위에서 하위로는 props 속성을 전달하여 데이터를 내려주고

하위에서 상위로는 이벤트를 발생시킨다.

 

2.상위컴포넌트 → 하위컴포넌트 데이터 전달

2-1. props

상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 사용하는 속성. 하위컴포넌트 속성에 정의한다

Vue.component('child-component',{

      props:['props속성이름']

}

상위 컴포넌트의 HTML 코드에 정의된 child-component 태그에 v-bind속성을 정의한다.

·         v-bind:프록스 속성 이름="상위컴포넌트의 데이터 이름"

 

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>Document</title>

</head>

<body>

    <div id="app">

        <child-component v-bind:propsdata="message"></child-component>

    </div>

</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

<script>

    Vue.component("child-component", {

        props : ["propsdata"], //props 속성 이름

        template : "<p>{{propsdata}}</p>"

    });

    new Vue({

        el : "#app",

        data : {

            message : "Hello World!!! This is parent message!! passed from Parent Component" //상위컴포넌트의 데이터 이름

        }

    })

</script>

</html>

 

3.하위컴포넌트 → 상위컴포넌트 이벤트 발생

3-1. 이벤트 발생과 수신

 하위컴포넌트에서 특정 이벤트가 발생하면(event emit) 상위컴포넌트로 신호를 보낸다.

 상위컴포넌트에서는 하위 컴포넌트의 특정 이벤트가 발생되기를 기다리고 있다가 해당 이벤트를 수신하여 데이터를 처리한다.

3-2. 이벤트 발생과 수신 형식

-발생 : $emit()

-수신 : v-on

규칙 : v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메서드이름"

버튼을 클릭하면, 하위컴포넌트에서 상위컴포넌트로 show-log 이벤트를 발생시키면서 파라미터문자열을 전송하고

상위컴포넌트는 하위컴포넌트로부터 받은 파라미터문자열을 console.log 출력시키는 예제.

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <title>Document</title>

</head>

<body>

    <div id="app">

        <child-component v-on:show-log="printText"></child-component>

    </div>

</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

<script>

    Vue.component("child-component", {

        template : "<button v-on:click='showLog'>show</button>",

        methods : {

            showLog : function(){

                this.$emit("show-log", "parameter is xxx!"); // 이벤트 발생

            }

        }

    });

 

    var app = new Vue({

        el : "#app",

        data : {

            message : "Hello Vue! passed from Parent Component"

        },

        methods : {

            printText : function(param){

                console.log(param);

            }

        }

    })

</script>

</html>

 

pass increase 이벤트를 방생시켜 Hi 로그와 상위컴포넌트에 저장되어있던 데이터 num 값을 +1 증가시키는 예제.

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <meta http-equiv="X-UA-Compatible" content="ie=edge">

  <title>Document</title>

</head>

<body>

  <div id="app">

    <p>{{ num }}</p>

    <!-- <app-header v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메서드 이름"></app-header> -->

    <app-header v-on:pass="logText"></app-header>

    <app-content v-on:increase="increaseNumber"></app-content>

  </div>

 

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>

    var appHeader = {

      template: '<button v-on:click="passEvent">click me</button>',

      methods: {

        passEvent: function() {

          this.$emit('pass');

        }

      }

    }

    var appContent = {

      template: '<button v-on:click="addNumber">add</button>',

      methods: {

        addNumber: function() {

          this.$emit('increase');

        }

      }

    }

 

    var vm = new Vue({

      el: '#app',

      components: {

        'app-header': appHeader,

        'app-content': appContent

      },

      methods: {

        logText: function() {

          console.log('hi');

        },

        increaseNumber: function() {

          this.num = this.num + 1;

        }

      },

      data: {

        num: 10

      }

    });

  </script>

</body>

</html>

 

 

4. 같은 컴포넌트 레벨 간의 통신 방법

4-1. 이벤트버스

위에서 아래로는 porps, 아래에서 위로는 이벤트발생을 통해 통신했다면,

같은 레벨의 컴포넌트는 통신이 되지 않으므로 부모 컴포넌트에게 이벤트를 올려주고 하위 컴포넌트에게 props 데이터를 내려주는 방법으로 통신해야한다.

(이와 같은 통신방식을 '이벤트버스' 라고 정의한다)

 

여기서 같은 레벨의 컴포넌트가 통신이 되지 않는 이유도 바로 컴포넌트 고유의 유효범위 때문이다.

위에서 컴포넌트는 자체 scope 가지고 있다고 했는데, 같은 이유로 컴포넌트는 다른 컴포넌트의 값을 직접 참조하지 못하므로 기본적인 통신규칙에 따라

같은 레벨의 컴포넌트가 통신을 하기 위해서는 공통 상위 컴포넌트로 이벤트를 전달 공통 상위 컴포넌트에서 하위로 props 내려 보내는 방식으로 통신해야한다.

 

 

4-2. 이벤트버스의 /단점

장점 : 이벤트버스를 활용하면 컴포넌트간 데이터전달이 가능하게 .

단점 : 컴포넌트가 많아지면 어디서 어디로 보냈는지 관리가 되지 않는 문제가 발생

 

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <meta http-equiv="X-UA-Compatible" content="ie=edge">

  <title>Document</title>

</head>

<body>

  <div id="app">

    <app-header v-bind:propsdata="num"></app-header>

    <app-content v-on:pass="deliverNum"></app-content>

  </div>

 

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <script>

    var appHeader = {

      template: '<div>header</div>',

      props: ['propsdata']

    }

    var appContent = {

      template: '<div>content<button v-on:click="passNum">pass</button></div>',

      methods: {

        passNum: function() {

          this.$emit('pass', 10);

        }

      }

    }

 

    new Vue({

      el: '#app',

      components: {

        'app-header': appHeader,

        'app-content': appContent

      },

      data: {

        num: 0

      },

      methods: {

        deliverNum: function(value) {

          this.num = value;

        }

      }

    })

  </script>

</body>

</html>