03.Vue.js Component
Updated:
1.Component Basics
- 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>
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>
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 workflow
-
MyBtn.vu
e 의component
에서input element
에 입력하게 되면 msg 라는 양방향 데이터 바인딩으로 script 부분으로 갱신이 됨 -
watch 를 통해 갱신되는 msg() 를 감시해서 변할때마다
this.$emit
(‘changeMsg
’) 가 실행이 됨 -
그렇게 되면 App.vue 의
@change-msg
가 실행이 되는것이고, 담에 logMsg 가 실행이 되어 parameter 는 양방향으로 연결되어 변경된 데이터로 바뀜 this.msg 로 -
그 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>
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>
🔶 🔷 📌 🔑
Reference
- Vue.js guide v3.0 - https://v3.vuejs.org/guide/introduction.html
Leave a comment