Today I Learned ... Vue.js
🙋♂️ Reference Book
DAY 04 - 220602
-8. 컴포넌트 심화
-9. Composition API
-10. Proxy 사용
ChildCompo.vue
<template>
<button @click="childFunc" ref="btn">Click</button>
</template>
<script>
export default {
methods: {
childFunc() {
console.log('부모가 발생시킨 이벤트');
}
}
}
</script>
ParentCompo.vue
<template>
<child-compo ref="child_compo" @send-message="sendMessage" />
</template>
<script>
import ChildCompo from './ChildCompo.vue';
export default {
components: [ChildCompo],
mounted() {
this.$refs.child_compo.$refs.btn.click();
},
};
</script>
부모 컴포넌트의 ref를 child_compo로 설정하여 $refs
로 접근할 수 있게 한다.
부모 컴포넌트에서 자식 컴포넌트를 ref로 접근하고, 자식 컴포넌트에서 ref로 사용하는 버튼에 접근할 수 있다.
부모 컴포넌트에서 자식 컴포넌트에 정의된 함수를 직접 호출 가능.
ChildCompo.vue
...
methods: {
callFromParent() {
console.log('부모가 호출한 함수');
}
}
ParentCompo.vue
<template>
<child-compo ref="child_compo" @send-message="sendMessage" />
</template>
<script>
import ChildCompo from './ChildCompo.vue';
export default {
components: {ChildCompo},
mounted() {
this.$refs.child_compo.callFromParent();
},
};
</script>
data 필드의 옵션 값을 변경할 수 있음.
ChildCompo.vue
<template>
<h1>{{ msg }}</h1>
</template>
<script>
export default {
data() {
return {
msg: '',
};
},
};
</script>
ParentCompo.vue
<template>
<child-compo ref="child_compo" />
<button @click="changeChildData">Change Child Data</button>
</template>
<script>
import ChildCompo from './ChildCompo.vue';
export default {
components: { ChildCompo },
methods: {
changeChildData() {
this.$refs.child_compo.msg = '제목 바꿔버리기';
},
},
};
</script>
ref로 접근하여 $refs
에서 데이터를 바꾸 수 있다.
= 커스텀 이벤트
$emit
을 사용한다.ChildCompo.vue
export default {
data() {
return {
msg: '자식이 부모에게 보내는 메시지',
};
},
mounted() {
this.$emit('send-message', this.msg);
// emit의 첫번째 인자는 부모 컴포넌트의 이벤트명 , 두번째 인자는 함수의 파라미터로 전달할 값.
// 자식이 Mount 되면 부모 컴포넌트의 send-message 이벤트를 호출하는 것.
},
};
ParentCompo.vue
<template>
<child-compo @send-message="sendMessage" />
</template>
<script>
import ChildCompo from './ChildCompo.vue';
export default {
components: { ChildCompo },
methods: {
sendMessage(data) {
console.log(data);
},
},
};
</script>
부모에서 커스텀 이벤트인@send-message
를 정의함.
-> 커스텀 이벤트는 자식 컴포넌트에서 $emit
으로 호출하게 됨.
부모 컴포넌트에 정의 된 sendMessage 함수가 실행되고,
자식 컴포넌트로부터 전달받은 데이터(msg)를 파라미터로 받아와서
부모 컴포넌트에서 사용 가능.
computed
를 사용하면 자식 컴포넌트의 데이터 값이 변할때ChildCompo.vue
<template>
<button @click="childFunc" ref="btn">자식 데이터 변경하기</button>
</template>
<script>
export default {
data() {
return {
msg: '기존 메시지',
};
},
methods: {
childFunc() {
this.msg = '변경된 메시지';
},
},
};
</script>
ParentCompo.vue
<template>
<button @click="checkChild">자식 데이터 조회</button>
<child-compo ref="child_compo" />
</template>
<script>
import ChildCompo from './ChildCompo.vue';
export default {
components: { ChildCompo },
computed: {
msg() {
return this.$refs.child_compo.msg;
},
},
methods: {
checkChild() {
alert(this.msg);
},
},
};
</script>
- computed = 기존 데이터 기반으로 새로운 데이터를 활용하기 위해 사용 데이터 값 하나만을 감시하기 위해 사용
- watch = 데이터 변경이 있어도 처음에 실행됨 실제 데이터 변경이 일어나기 전까지는 실행되지 않음.
컴포넌트 내에서 다른 컴포넌트를 사용할 때 쓰는 마크업을 재정의, 확장.
컴포넌트의 재활용성을 높여주는 기능.
<div class="container">
<header></header>
<main></main>
<footer></footer>
</div>
-> 기본적인 모달의 마크업 구성.
모달의 기본 틀에 해당하는 컴포넌트를 Slot을 이용하여 만들고,
컨텐츠에 해당하는 부분만 작성하면 통일성이 있음.
(동일한 사용자경험 UX를 주기 위함)
modalLayout.vue
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
-> Slot에 name을 지정하여 사용하는 방법. (Named Slots)
slot을 사용하는 컴포넌트에서는 v-slot: 슬롯명
디렉티브를 사용하여 해당 slot 으로
html 코드를 삽입할 수 있음.
(name이 없는 경우 v-slot:default
)
SlotUseModalLayout.vue
<modal-layout>
<template v-slot: header>
<h1>팝업 타이틀</h1>
</template>
<template v-slot: default>
<p>팝업 컨텐츠 1</p>
<p>팝업 컨텐츠 2</p>
</template>
<template v-slot: footer>
<button>닫기</button>
</template>
</modal-layout>
v-slot
대신에 단축어로 #
로 적어줘도 된다.
<template #header> ... </template>
props
를 이용한다면? 굉장히 비효율적이고 복잡한 코드가 된다.-> 이때, Provide와 Inject를 사용할 수 있음.
부모 컴포넌트에는 provide 옵션을,
자식 컴포넌트에는 inject 옵션을 통해 데이터 쉽게 전달 가능!
React에서 Provider / Consumer 구조였던 Context API와 유사한 기능임.
ProvideInject.vue
<template>
<ProvideInjectChild />
</template>
<script>
import ProvideInjectChild from './ProvideInjectChild';
export default {
components: { ProvideInjectChild },
data() {
return {
items: ['A', 'B'],
};
},
provide() {
return {
itemLength: this.items.length,
};
},
};
</script>
ProvideInjectChild.vue
<template>
<div></div>
</template>
<script>
export default {
inject: ['itemLength'],
mounted() {
console.log(this.itemLength);
},
};
</script>
provide() {
return {
itemLength: this.items.length,
};
inject: ['itemLength']
inject를 통해 데이터를 전달받는 자식 컴포넌트에서는
전달받은 데이터가 어떤 부모로부터 전달되는지 확인할 수 X.
HTML 어트리뷰트는 대소문자 구분이 없기 때문에 브라우저는 대문자를 소문자로 변경하여 읽음.
(예 - userName을 username 으로)
그렇기 때문에 카멜 케이스(대소문자 혼용)로 prop의 이름을 정한 경우에,
DOM 템플릿 안 (template 태그 내) 에서는 케밥 케이스
를 사용하여야 올바르게 동작합니다.
Vue.component('blog-post', {
// camelCasef로 작성해줌
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
<blog-post post-title="hello!"></blog-post>