본문 바로가기
front-end/Vue.js

Vue.js 컴포넌트

by 모두의 향연 2022. 7. 11.
728x90
반응형

SFC(single file application)

 

✔️컴포넌트 (Component)란?

  • Vue의 가장 '강력한 기능' 중 하나
  • HTML Element를 확장하여 재사용 가능한 코드를 캡슐화
  • Vue Component는 Vue Instance이기도 하기 때문에 모든 옵션 객체를 사용
  • Life Cycle Hook 사용 가능
  • 전역 컴포넌트와 지역 컴포넌트

 

✔️전역컴포넌트와 지역 컴포넌트

📌전역  컴포넌트

: 전역 컴포넌트를 등록하려면, Vue.component('tagName',{ options}),

html에서 컴포넌트를 불러올 때 권장하는 컴포넌트 이름 : 케밥 표기법 ( 전부 소문자, - ),

전역이라서 다른 Vue영역에서도 사용 가능

<body>
  <div id="app1">
    <global-com></global-com>
    <global-com></global-com>
  </div>
  <div id="app2">
    <global-com> </global-com>
    <global-com></global-com>
  </div>
  <script>
    //전역 컴포넌트
    Vue.component('GlobalCom', {
      template: `<h2>전역 컴포넌트입니다.</h2>`,
    }),
      new Vue({
        el: '#app1',
      });
    new Vue({
      el: '#app2',
    });
  </script>
</body>

 

📌지역 컴포넌트

: Vue의 해당 el영역 안에서만 사용 가능

<body>
  <div id="app1">
    <local-com></local-com>
    <local-com></local-com>
  </div>
  <div id="app2">
    <local-com></local-com>
    <local-com></local-com>
  </div>
  <script>
    new Vue({
      el: '#app1',
      components: {
        'LocalCom': {
          template: `<h2>지역 컴포넌트입니다.</h2>`,
        }
      },
    });
    new Vue({
      el: '#app2',
    });
  </script>
</body>

 

✔️컴포넌트 template

- div가 저절로 만들어진다?=꼭 적어줘야 하는 것(탬플릿 안에는 무조건 1개의 div만 가능)

- template의 id를 적을 수 있다

- 컴포넌트도 Vue인스턴스화 시킬 수 있어서 data 가능하다(컴포넌트 안에서 data를 쓸 경우 무조건 function형태로 써야 함)

<body>
  <div id="app">
    <my-comp></my-comp>
  </div>
  <template id="MyTem">
    <div>
      <h2>{{msg}}</h2>
    </div>
  </template>

  <script>
    Vue.component('MyComp', {
      template: '#MyTem',
      data() {
        return {
          msg: 'hello component',
        };
      },
    });

    new Vue({
      el: '#app',
    });
  </script>
</body>

Vue 컴포넌트 안에서 탬플릿을 길게 쓸 수 없으니 밖으로 뺀 거고, 그게 다시 탬플릿 안으로 들어가고 전체 완성된 컴포넌트가 다시 완성된다. 

 

 

✔️Component data 공유 문제

버튼을 클릭하면 count가 올라가는 형태가 3개 똑같이 생긴다. 근데 각각 다르게 count가 되는 게 아니고 똑같이 카운트가 올라간다. 이유: 전역 변수인 num을 3개의 컴포넌트가 공유해서 쓰기 때문

해결책: 전역 변수가 아닌 지역변수로 바꿔주기

 

<body>
  <h2>컴포넌트 데이터 공유 문제 해결</h2>
  <div id="app">
    <count-view></count-view>
    <count-view></count-view>
    <count-view></count-view>
  </div>
  <template id="CountView">
    <div>
      <span>{{ count }}</span>
      <button @click="count++">클릭</button>
    </div>
  </template>
  <script>
    Vue.component('CountView', {
      template: '#CountView',
      data() {
        return {
          count: 0,
        };
      },
    });
    new Vue({
      el: '#app',
    });
  </script>
</body>

 

 

 

 

✔️컴포넌트간 통신

: 상위 컴포넌트와 하위컴포넌트 간의 data전달 방법

  • 부모 → 자식 : props라는 특별한 속성을 전달(Pass Props)
  • 자식 → 부모 : event로만 전달 가능(Emit Event)

 

 

 

📌상위에서 하위 컴포넌트로 data 전달

  • 하위 컴포넌트는 상위 컴포넌트의 값을 직접 참조 불가능
  • data와 마찬가지로 props 속성의 값을 template에서 사용이 가능

props 여러개 넘길 수 있으니까 객체타입+상세설정도 가능(권장)

<body>
  <div id="app">
    <h2>props test</h2>
    <child-component propsdata="안녕하세요"></child-component>
  </div>
  <script>
    //하위 컴포넌트
    Vue.component('childComponent', {
      props: ['propsdata'],
      template: '<span>{{ propsdata }}</span>',
    });
    new Vue({
      el: '#app',
    });
  </script>
</body>

 

 

📌input창이랑 props 데이터의 연결하기:v-bind연결하기

<body>
  <div id="app">
    <h2>props test</h2>
    <input type="text" v-model="msg">
    <child-component :propsdata="msg"></child-component>
  </div>
  <script>
    //하위 컴포넌트
    Vue.component('childComponent', {
      props: ['propsdata'],
      template: '<span>{{propsdata}}</span>',
    });
    new Vue({
      el: '#app',
      data() {
        return {
          msg: "",
        }
      }
    });
  </script>
</body>

 

 

📌동적 props : 상위에서 하위로 넘겨줄 때 이름자체로 넘겨주는것과 상위 컴포넌트에 있는 data를 넘겨줄 때는 v-bind써주기

<body>
    <div id="app">
        <child-comp area="서울" v-bind:msg="msg[parseInt(Math.random() * 5)]"></child-comp>
        <child-comp area="대전" v-bind:msg="msg[parseInt(Math.random() * 5)]"></child-comp>
        <child-comp area="대구" v-bind:msg="msg[parseInt(Math.random() * 5)]"></child-comp>
        <child-comp area="부산" v-bind:msg="msg[parseInt(Math.random() * 5)]"></child-comp>
        <child-comp area="광주" v-bind:msg="msg[parseInt(Math.random() * 5)]"></child-comp>
    </div>

    <template id="ChildComp">
        <div>
            <h2>SSAFY {{area}}지역 {{msg}}</h2>
        </div>
    </template>

    <script>
        Vue.component('childComp', {
            props: ['area', 'msg'],
            template: '#ChildComp',
        });
        new Vue({
            el: '#app',
            data: {
                msg: ['최고', '굿', '좋아', '짱', '멋지다'],
            },
        });
    </script>
</body>

 

 

📌동적 props for문 사용

<body>
    <div id="app">
        <child-comp v-for="(area, i) in areas" :key="i" :area="area" :msg="msg[parseInt(Math.random() * 5)]">
        </child-comp>
    </div>

    <template id="ChildComp">
        <div>
            <h2>SSAFY {{area}}지역 {{msg}}</h2>
        </div>
    </template>

    <script>
        Vue.component('childComp', {
            props: {
                'area': String,
                'msg': {
                    type: String, require: true
                }
            },
            template: '#ChildComp',
        });
        new Vue({
            el: '#app',
            data: {
                areas: ['서울', '대전', '대구', '부산', '광주'],
                msg: ['최고', '굿', '좋아', '짱', '멋지다'],
            },
        });
    </script>
</body>

 

 

📌객체로 props 전달하기

<body>
    <h2>컴포넌트 객체 데이터 전달</h2>
    <div id="app">
        <member-view v-bind:member="user"></member-view>
    </div>

    <template id="MemberView">
        <div>
            <div>이름 : {{ member.name }}</div>
            <div>나이 : {{ member.age }}</div>
            <div>이메일 : {{ member.email }}</div>
        </div>
    </template>

    <script>
        Vue.component('memberView', {
            props: ['member'],
            template: '#MemberView',
        });

        new Vue({
            el: '#app',
            data() {
                return {
                    user: {
                        name: '모두의 향연',
                        age: 100,
                        email: 'feastforall@tistory.com',
                    },
                };
            },
        });
    </script>
</body>

 

✔️사용자 정의 이벤트

: v-on 이벤트 리스너는 항상 자동으로 소문자 변환되기 때문에 v-on:myEvent 는 자동으로 v-on:myevent 로 변환된다. 이름이 my-event일 경우 myEvent 이벤트를 들을 수 없다.

[이벤트 발생]
vm.$emit(“이벤트명”, [… 파라미터]);
ex) vm.$emit(“speed”, 100);

[이벤트 수신]
vm.$on(“이벤트명”, 콜백함수( ){ });
ex) vm.$on(“speed”, function(speed) { });
<body>
  <h2>사용자 정의 이벤트</h2>
  <div id="app">
    <button v-on:click="doAction">메시지전송</button>
    <h2>{{message}}</h2>
  </div>

  <script>
    new Vue({
      el: '#app',
      data: {
        message: '',
      },
      methods: {
        doAction() {
          this.$emit('sendMsg', '안녕하세요 여러분');
        },
      },
      created() {
        this.$on('sendMsg', (msg) => {
          alert(msg);
          this.message = msg;
        });
      },
    });
  </script>
</body>

 

 

✔️하위에서 상위 컴포넌트로 event 전달

하위 컴포넌트에서 상위 컴포넌트가 지정한 이벤트를 발생 ($emit)
ex) this.$emit(“이벤트명”);

상위 컴포넌트는 하위 컴포넌트가 발생한 이벤트를 수신 (on)하여 data 처리
ex) <child v-on:이벤트명=“상위 컴포넌트 메소드명“></child>
<body>
    <div id="app">
        <h2>하위에서 상위 컴포넌트로 event 전달</h2>
        <h2>총 투표수 : {{ total }}</h2>
        <subject v-on:addtotcount="addTotalCount" title="코딩"></subject>
        <subject v-on:addtotcount="addTotalCount" title="알고리즘"></subject>
    </div>

    <script>
        Vue.component('Subject', {
            template: '<button v-on:click="addCount"> {{title}} - {{ count }}</button>',
            props: ['title'],
            data: function () {
                return {
                    count: 0,
                };
            },
            methods: {
                addCount: function () {
                    this.count += 1;
                    this.$emit('addtotcount');
                },
            },
        });

        new Vue({
            el: '#app',
            data: {
                total: 0,
            },
            methods: {
                addTotalCount: function () {
                    this.total += 1;
                },
            },
        });
    </script>
</body>

 

 

✔️비 상하위간 통신

: 비어 있는 Vue Instance 객체를 Event Bus로 사용

: 복잡해질 경우 상태관리 라이브러리인 Vuex 사용 권장

var bus = new Vue();

// 컴포넌트 A bus.$emit(‘id-selected’, 1);

// 컴포넌트 B bus.$on(‘id-selected’, function (id) { });
<body>
  <div id="app">
    <my-count></my-count>
    <log></log>
  </div>

  <template id="myCount">
    <div>
      <input type="text" v-model.number="count" @keyup.enter="send" />
      <button @click="send">보내기</button>
    </div>
  </template>

  <template id="log">
    <div>
      <h2>{{count}}</h2>
      <ul>
        <li v-for="msg in list">{{msg}}</li>
      </ul>
    </div>
  </template>

  <script>
    const bus = new Vue();

    Vue.component('myCount', {
      template: '#myCount',
      data() {
        return {
          count: 0,
        };
      },
      methods: {
        send() {
          bus.$emit('updateLog', this.count);
          this.count = '';
        },
      },
    });

    Vue.component('Log', {
      template: '#log',
      data() {
        return {
          count: 0,
          list: [],
        };
      },
      methods: {
        updateLog(data) {
          this.count += data;
          this.list.push(`${data}을 받았습니다.`);
        },
      },
      created: function () {
        bus.$on('updateLog', this.updateLog);
      },
    });

    new Vue({
      el: '#app',
    });
  </script>
</body>
728x90
반응형