개발은 재밌어야 한다
article thumbnail
Published 2021. 4. 13. 16:56
Vue.js 컴포넌트 (심화) javascript/Vue
반응형

단일 컴포넌트

# 전역수준 컴포넌트 문제점

컴포넌트 관리가 어렵고 재사용성이 낮아진다.

  • 빌드 단계가 없으므로 ES2015, TypeScript와 같은 최신 버전 자바스크립트 문법을 사용할 수 없다. (ES 2015 전 문법 작성만 가능)
  • CSS를 지원하지 않는다. 컴포넌트들은 고유한 스타일 정보를 포함하는 경우가 많으나, 전역 컴포넌트에서는 CSS 스타일을 빌드하고 모듈화하는 기능을 제공하지 않는다.
    • css를 <link>방식이 아니라 모듈 처럼 참조(import)할 수 없다. → 모듈 처럼 사용할 경우 빌드 거치면 자체적으로 link 태그를 만든다.
  • 컴포넌트들의 템플릿이 작성될 때, HTML 파일 안에 여러 개의 <template/> 태그가 작성되어 식별하기 어렵다. 또한 템플릿마다 고유한 ID를 부여하고 컴포넌트들도 고유한 이름을 지정해야 한다.

# 단일 파일 컴포넌트

확장자가 .vue인 파일을 컴포넌트 단위로 생성한다. 크게 3가지로 나누어짐

  • <template>
  • <script>
  • <style> // 클래스 기반으로 작성해야 컴포넌트 기반 (모듈) 스타일 적용이 가능함.

사용할 컴포넌트는 등록해줘야 사용이 가능하다.
등록 방법은 전역 등록과 지역 등록이 있다.

dreamcoding.tistory.com/8?category=952284 참고

 

Vue.js 컴포넌트 (기본)

컴포넌트란? 컴포넌트 그 자체로 제 기능을 하며 재사용할 수 있는 컴포넌트로 구성된 대규모 응용 프로그램을 구축할 수 있게 해주는 추상적 개념이다. 기본 HTML Element를 확장하여 재사용 가능

dreamcoding.tistory.com

# 전역 컴포넌트와 차이점

  • <template>에 id 특성을 부여하지 않는다.
  • <script>영역에서는 Vue컴포넌트의 template을 지정하지 않는다.
  • Vue.component로 이름과 template 속성을 지정하지 않는다.
  • name 속성은 지정할 수 있다.
    • name은 컴포넌트 파일명과 동일하게 작성하는 것이 좋음.
    • import한 모듈명이 아닌 설정한 name으로 template 가져다 쓸 수 있음 → 모듈명과 정확한 상관관계 확인 필요
      • ex) script: { components: ListComponent }
      • ex) html: <list-component></list-component>
  • 반드시 적어야 되는건 아니지만 name 옵션을 설정해야 vue devtools에서 디버깅할 때 이름이 나오므로 지정하는 것이 좋음.
  • 하나의 단어일 경우 별도 name 작성은 필요없다.
  • 반드시 객체를 export해야 한다.
  • 컴포넌트에서 사용할 스타일은 <style> 내부에서 작성한다.

단일 컴포넌트

<template>
   <div id='app'> .. </div>
</template>
 
<script>
export default {
   name: 'app',
   data() {
      return {
           msg: 'test'
      }
   }
}
</script>
 
<style>
  #app { .. }
</style>

단일 컴포넌트 사용

import Vue from 'vue'
import TodoList from './components/TodoList'
 
Vue.config.productionTip = false
 
/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { TodoList }, // 지역 등록
  template: '<TodoList/>'
})

컴포넌트 스타일

특정 컴포넌트만의 스타일 지정할 수 있으며, 다음과 같은 방법이 있다.

  • 범위 CSS (Scoped CSS)
  • CSS 모듈(CSS Module)

# 범위 CSS (Scoped CSS)

<style scoped>로 설정 특성 선택자(attribute selector)를 사용하기 때문에 브라우저에서 스타일을 적용하는 속도가 느리다. 그렇기 때문에 반드시 속도가 빠른 ID 선택자, 클래스 선택자, 태그명 선택자로 요소를 선택해 스타일을 적용하도록 해야한다.

  • traverse 속도 빠른것: id, class, tag
  • traverse 속도 느린것: attr ([])

부모 컴포넌트에 적용된 범위 CSS는 하위 컴포넌트에도 반영된다. (CSS 범위가 자식/자손 컴포넌트까지 적용됨)

 

<style scoped>
   .main {..}
</style>
 
<div class="main">..</div>
--------------------------------------------------------
// 실제 브라우저에서 보면 element에 data-v로 시작하는 속성이 추가됐음을 확인 할 수 있음.
<div data-v-1049f479 class="main">..</div>
.main[data-v-1049f479] { ...}

# CSS 모듈 (CSS Module)

<style module>로 설정 CSS 모듈은 CSS스타일을 마치 객체 처럼 다룰 수 있게 한다.
장점

  • 이름 충돌이 나지 않는다.
  • 클래스 선택자를 사용할 수 있다.


단점

  • 자식 컴포넌트로 스타일이 전이되지 않는다.
  • 비슷한 스타일을 여러 컴포넌트에서 작성할 경우 CSS 파일 크기가 커진다.
<style module>
  .main {..}
</style>
------------------------------------------------------------------------------------------
<button :class="$style.main">
<button :class="[$style.box, $style.border]"> // 배열로 여러개 적용 가능
------------------------------------------------------------------------------------------
// 실행 결과
<button class="_2KXmN....."> // ... main이 아닌 충돌나지 않도록 다른 이름 사용

슬롯 (Slot)

부모 컴포넌트에서 자식 컴포넌트로 HTML 마크업을 전달할 수 있게 해준다.

  • props(속성)으로 전달해야하는 정보가 HTML 태그가 포함된 문자열이라면 사용하기가 어려우므로, 여러 개의 적용하려는 CSS 클래스가 있다면 이것들을 속성을 통해 하나씩 전달하기 위해 child 컴포넌트에 많은 props가 정의되어야 하므로 보통 슬롯을 사용

# 단일 슬롯

자식 컴포넌트에 하나의 slot만 전달한다.

Child

<template>
  <div> .. 
      <slot></slot>
  </div>
</template>

Parent

<template>
   <child> <div> .. </div> </child>
</template>

 

# 명명된 슬롯 (Named Slot)

슬롯에 이름을 부여하면 여러 개의 슬롯을 작성할 수 있다.

Child

<template>
   <div id="test">
     <header> <slot name="header"> </slot> </header>
     <aside id="sidebar"><slot name="sidebar"></slot></aside>
     ...
   </div>
</template>


Parent

<template>
   <div id="app">
       <layout>
            <h1 slot="header">헤더 영역</h1>  // 컴포넌트로도 처리 가능 <child1 slot="slot1"> 
            <div slot="sidebar"><ul> .. </ul> </div>
       </layout>
   </div>
</template>

# 범위 슬롯 (Scoped Slot)

자식 컴포넌트에서 부모 컴포넌트로 Slot에 속성을 전달하여 부모 컴포넌트에 출력할 내용을 커스터마이징 할 수 있다.

Child

<template>
  <div class="child">
      x: <input .. /><br/>
      <slot name="type1" :cx="x" :cy="y"></slot>
      <slot name="type2" :cx="x" :cy="y"></slot>
  </div>
</template>


Parent

<template>
   <child>
      <template slot="type1" scope="p1"> <div> {{p1.cx}} + {{p1.cy}} </div></template> // child의 x, y 속성을 cx, cy속성으로 전달받아 p1에서 참조
      <template slot="type2" scope="p2"> <div> {{p2.cx}} 더하기 {{p2.cy}} </div></template>
   </child>
</template>

동적 컴포넌트 (Dynamic Component)

화면의 동일한 위치에 특정 상황에 따라 각각 다른 컴포넌트를 표현해야할 경우 동적 컴포넌트를 사용한다.
<component>요소를 템플릿에 작성하고 v-bind 디렉티브를 이용해 is 특성값으로 어떤 컴포넌트를 그 위치에 나타낼지 결정한다.

<ul>
    <li><a href="#" @click="changeMenu('about')">About</a></li>
    <li><a href="#" @click="changeMenu('home')">home</a></li>
    <li><a href="#" @click="changeMenu('contact')">Contact</a></li>
</ul>
 
<div class="container">
    <component v-bind:is="currentView"></component>
</div>
 
 
<script>
import Home from './cmp/Home.vue'
import Contact from './cmp/Contact.vue'
import About from './cmp/About.vue'
 
export default {
  data() { return { currentView: 'home'; } },
  methods: { changeView(view) { this.currentView = view; } }
}
</script>

<keep-alive> 요소

  • 매번 실행되므로(Q) <component>로 표현될 자식 컴포넌트들이 정적 콘텐츠라면 매번 실행되는 것은 효율적이지 않다. 이런 경우는 <component>요소를로 감싸서 캐싱하면 된다.
  • <keep-alive> : 렌더링된 결과를 캐싱하고 사용함. (ex) 고정적인 Header와 같은 것에 사용
  • 만일 특정 컴포넌트만 캐싱하거나 캐싱하고 싶지 않다면 include, exclude 특성으로 컴포넌트들을 콤마로 구분해서 나열하면 된다. 이때 지정된 이름이 있어야 하므로 각 컴포넌트,마다 name 옵션을 부여해야하며 이 이름으로 include, exclude 한다.
    • name옵션을 부여하지 않으면 컴포넌트명 기준으로 name을 인식한다.
    • name은 대소문자를 구분한다
<keep-alive include="aoout, home">
  <component v-bind:is="currentView"></component>
</keep-alive>

재귀 컴포넌트

템플릿에서 자기 자신을 호출하는 컴포넌트.
반드시 name 옵션을 지정해야한다.

Child

<template>
  <ul>
      <li v-for="s in subs" v-bind:class="s.type">
          {{name}}
          <tree :subs="s.subs"></tree>
      </li> 
 </ul>
</template>
 
<script>
export default {
   name: 'tree',
   props: ['subs']
} 
</script>

Parent

<template>
  <div>
     ... 
     <tree :subs="orgcharts"></tree>
  </div>
</template>
 
<script>
import Tree from './Tree.vue'
 
export default{
  name: 'aobut',
  components: { Tree },
  data(){ 
    return {
        orgcharts: [ { name: '서울', type: 'division', subs: [ { name: '서울 동작구팀', type: 'team'}, ..] } ... ]
    }
}

 

참고 : Vue.js 퀵스타트

반응형
profile

개발은 재밌어야 한다

@ghyeong

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!