03.Vue.js Component

Updated:


1.Component Basics

Component 기초 자세히 보기..

  • Component 장점: 짜놓은 코드를 재사용해서 사용할 수 있습니다.

Component props

  • Props 기능을 사용해서 부모 - 자식 Component 끼리 특정한 데이터를 전달해서 사용할 수 있습니다. (부모-자식 간의 데이터 통신)

  • 해당 Component 에 HTML 의 속성처럼 내용이 붙어 있을 때, 내용의 값이 연결이 되어져 있을 때, 그것을 Component 내부에서 어떻게 처리 할지를 정의하는 개념으로 Props 를 사용할 수 있습니다.

// in MyBtn.vue (root/component 경로)

<template>
  <!-- slot tag 를 사용함으로써 App.vue 의 HTML 부분을 tag 로 사용하여 그안에를 tag 로 사용할 수 있게 된다 -->
  <div
    :class="{ large }"
    :style="{ backgroundColor: color }"
    class="btn">
    <slot></slot>
  </div>
</template>

<script>
export default {
  props: {
    color: {
      type: String,
      default: 'gray'
    },
    large: {
      type: Boolean,
      default: false
    }
  }
}
</script>


<style lang="scss" scoped>
  .btn {
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
    &.large {
      font-size: 20px;
      padding: 10px 20px;
    }
  }

</style>
// in App.vue

<template>
  <!-- span tag 는 myBtn.vue 파일의 slot tag 부분으로 들어 갈수 있고, 나머지 속성들도 다 slot 영역으로 들어갈 수 있다-->
  <MyBtn>Banana</MyBtn>
  <MyBtn color="#000">
    <span style="color:red;">Apple
    </span>
  </MyBtn> 
  <MyBtn
    large
    color="royalblue">
    Cheery
  </MyBtn> 
  <MyBtn>Banana</MyBtn> 
  <button>
    Banana
  </button>
</template>

<script>
import MyBtn from '~/components/MyBtn'

export default {
  components: {
    MyBtn
  },
  data() {
    return {
      color: '#000'
    }
  }
}
</script>

image

2.속성 상속

  • template 에서 최상위 요소(root 경로)에 한가지만 남겨 둘경우, class 가 상속이 될 수 있음

  • 만약 2개 이상일 경우에는 상속이 되지 않습니다.

  • 즉, 하나의 Component 를 만들때, 그 내부에서 root element 가 1개 인지, 2개인지 확인을 하고 코드를 작성해야 합니다.

  • Component 가 가질수 있는 속성들을 특정한 내부 요소에 사용될 수 있습니다 (v-bind="$attrs" 를 사용하여 원하는곳에 상속받은 element 들을 사용할 수 있습니다.)

// in App.vue 

<template>
  <MyBtn
    class="jacob"
    style="color: red;"
    title="Hello world!">
    Banana
  </MyBtn>
</template>


<script>
import MyBtn from '~/components/MyBtn'
export default {
  components: {
    MyBtn
  }
}
</script>
// in MyBtn.vue (상속 받은것을 확인 할 수 있습니다.)

<template>
  <!-- 상위경로 (최상위 요소 또는 root 경로 라고 함) 가 2개가 있는 상태임 => div tag 가 2개가 있는것-->
  <!-- 실제로 상속되는 것을 $attrs 로 지정하여 원하는 곳에서 넣어서 사용할 수 있는것을 말함 -->
  <!-- 상속 받기 위해서는 약어 없이 v-bind="$attrs" 를 사용하여 원하는 곳에 사용될 수 있음 -->
  <div class="btn">
    <slot></slot>
  </div>
  <h1 v-bind="$attrs"></h1>
</template>



<script>
export default {
  inheritAttrs: false, // false 로 해 놓으면 어떠한 속성들도 상속하지 않음
  created() { // created() 는 life cycle 이 생성된 직후에 실행되는것
    console.log(this.$attrs) // App.vue 에서 넘어온 class="jacob", style="color:red;" 를 확인 할 수 있음
  }
}
</script>


<style lang="scss" scoped>
  .btn {
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
    &.large {
      font-size: 20px;
      padding: 10px 20px;
    }
  }

</style>

image

3.Emit

  • 위 Chapter 에서 $attrs 을 적용해서 선택적으로 상속 하는과 같이 @click 과 같은 event 를 선택적으로 상속받기 위해서는 $emit() 를 사용할 수 있습니다.

  • Component 의 연결하는 event 는 실제로 쓰이는 event 의 이름이 아니여도 상관이 없습니다. 원하는 event 의 이름을 정한다음에 component 의 emits 에서 받아서 언제 실행 할 것인지 정의를 해주기만 하면 실행이 됩니다.

// in App.vue

<template>
  <MyBtn
    @click="log"
    @change-msg="logMsg">
    Banana
  </MyBtn>
</template>

<script>
import MyBtn from '~/components/MyBtn'
export default {
  components: {
    MyBtn
  },
  methods: {
    log() {
      console.log('Click!!')
      console.log(event)
    },
    logMsg(msg) {
      console.log(msg)
    }
  }
}
</script>
// in MyBtn.vue

<template>
  <!-- event 를 $emit() 으로 연결하면 선택된 tag 에만 선택적으로 상속으로 받아서 event 가 실행됨 -->
  <!-- 즉, h1 을 클릭하게 되면 부모 요소의 @click="log" 가 실행되는것이다 -->
  <!-- @dblclick 은 더블클릭 event 임 -->
  <div class="btn">
    <slot></slot>
  </div>
  <h1 @dblclick="$emit('click', $event)">
    ABC
  </h1>
  <input
    type="text"
    v-model="msg" />
</template>



<script>
export default {
  inheritAttrs: false, // 상속이 되지 않아서 App.vue 에서 @click 이 실행되지 않음
  emits: [ // 뒤에 s 붙는거 주의 
    'click',
    'changeMsg'
  ],
  data() {
    return {
      msg: ''
    }
  },
  watch: {
    msg() {
      this.$emit('changeMsg', this.msg)
    }
  }
}
</script>


<style lang="scss" scoped>
  .btn {
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
    &.large {
      font-size: 20px;
      padding: 10px 20px;
    }
  }

</style>

emit

  • emit workflow

    1. MyBtn.vue 의 component 에서 input element 에 입력하게 되면 msg 라는 양방향 데이터 바인딩으로 script 부분으로 갱신이 됨

    2. watch 를 통해 갱신되는 msg() 를 감시해서 변할때마다 this.$emit(‘changeMsg’) 가 실행이 됨

    3. 그렇게 되면 App.vue 의 @change-msg 가 실행이 되는것이고, 담에 logMsg 가 실행이 되어 parameter 는 양방향으로 연결되어 변경된 데이터로 바뀜 this.msg 로

    4. 그 value 가 console 에 출력이 되는 구조 입니다.

4.Slot

  • Component 를 사용할때, 기본적인 contents 를 삽입하게 되면 해당하는 slot tag 가 대체 되서 해당하는 contents 로 삽입이 되는데, 만약에 contents 가 없는 경우에는 대신해서 slot tag 사이에 있는 contents 가 삽입이 됩니다.

    • slot 사이에있는 contents 를 Fallback Contents (대체 내용) 라고 불립니다.
  • Named Slots (이름을 가지는 슬롯)

** 참고로 약어 정리

- `v-bind` 는 => :
- `v-on` 는 => @
- `v-slot` 는 => #
// in App.vue

<template>
  <!-- 코드 순서대로 (B) 가 나중에 나오는데 named slot 을 사용해서 (B) 의 위치를 지정할 수 있다 -->
  <!-- v-slot: 의 약어로 # 가 사용됨, 코드 위치가 바꼈는데도 불구하고 named slot 에서 지정한 순서대로 출력이 됨 -->
  <MyBtn>
    <template #text>
      <span>Banana</span>
    </template>
    <template #icon>
      <span>(B)</span>
    </template>
  </MyBtn>
</template>

<script>
import MyBtn from '~/components/MyBtn'
export default {
  components: {
    MyBtn
  }
}
</script>
<template>
  <!-- slot 사이에 있는 text 가 출력되서 나타나게 됨 -->
  <div class="btn">
    <slot name="icon"></slot>
    <slot name="text"></slot>
  </div>
</template>


<style lang="scss" scoped>
  .btn {
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
    &.large {
      font-size: 20px;
      padding: 10px 20px;
    }
  }

</style>

image

5.Provide, Inject

// in App.vue

<template>
  <button @click="message= 'Good!'">
    Click!
  </button>
  <h1>App: </h1>
  <Parent />
</template>

<script>
import Parent from '~/components/Parent'
import { computed } from 'vue'

export default {
  components: { 
    Parent
  },
  data() {
    return {
      message: 'Hello world!'
    }
  },
  provide() {
    return { // Parent 를 Child 로 가기위한 중간 매개체로 사용할 필요가 없어짐
      msg: computed(() => this.message) // 하나의 계산된 데이터를 만들어서 반환을 시켜 줄수 가 있음 그 반환된 내용이 msg 로 들어가서 동작을 할 수 있게 됨 
    }
  }
}
</script>
// in Parent.vue

<template>
  <Child />
</template>

<script>
import Child from '~/components/Child'

export default {
  components: {
    Child
  },
}
</script>
// in Child.vue

<template>
  <div>Child: </div>
</template>

<script>
export default { // App.vue 에서 provide 된 data 가 inject 를 통해서 data 타입형식으로 받아서  로 할당되서 화면에 출력하게 됨
  inject: ['msg']
}
</script>
  • 위의 코드 workflows:

    • App.vue 에서 data() 로 message 를 정의를 했고, 그걸을 Child component 에서 출력하기 위해서 Props 를 통해서 한단계씩 내려주고 있습니다 (App.vue -> Parent.vue -> Child.vue)
  • Provide 를 사용 할때는 반응성을 제공할 수 없습니다.그래서 provide 를 사용할 경우에는 간단하게 data를 전달해서 한번 출력하는 용도로 만들거나, 반응성을 유지하기 위해서는 추가적인 작업을 해줘야 합니다.

  • Provide 에서 반응성을 유지해서 만들어 주려면 Computed() 를 이용해서 return 값을 callback 해서 만들 수 있습니다.

  • 정리 하자면, child 라는 component 는 부모의 부모인 App.vue 를 조상 component 라고 부름 , 조상에서 자식으로 data 를 물려 줄때는 props 속성을 사용함

  • 매개체 역활을 하는 parent.vue 의 역활도 있어야지 child 까지 data가 전송이 됨, 그래서 그과정을 생략하기 위해서 provide(), inject()를 사용하게 됨 => data를 빠르게 전달 할 수 있습니다. (단! 반응성을 가지지 않게 됨 그래서 computed 를 사용해서 반응성을 다시 사용할 수 있게 됨)

5.Refs

  • Component 를 참조 할 수 있는 기능 입니다.
// in App.vue

<template>
  <!-- ref 는 특정한 이름의 요소를 참조하겠다라는 의미를 가지고 있음 -->
  <!-- refs 기능은 HTML 연결이 되고 나서 후의 mounted() 에서만 사용이 가능 (created() 는 안됨) -->
  <h1 ref="hello">
    Hello World!
  </h1>
</template>

<script>
export default {
  created() { // undefined 으로 나옴
    console.log(this.$refs.hello)
  },
  mounted() { // refs 는 HTML 연결 되고나서야 사용 할 수 있음
    console.log(this.$refs.hello) // 위의 ref 라는 요소를 참조하여 출력할 수 있음
  }
}
</script>
  • 실제 하위 Component 를 연결해서 refs 하는 방법
// in App.vue

<template>
  <Hello ref="hello" />
</template>

<script>
import Hello from '~/components/Hello'
export default {
  components: {
    Hello
  },
  mounted() { 
    console.log(this.$refs.hello.$refs.good) // Hellp.vue 의 ref 의 명시된 이름을 통해 참조해서 가져올수 있음
  }
}
</script>
// in Hello.vue

<template>
  <h1>Hello~~</h1>
  <h1 ref="good">
    Good?
  </h1>
</template>

image

🔶 🔷 📌 🔑

Reference

Categories:

Updated:

Leave a comment