- 케밥 케이스 : 소문자, 대시로
- 파스칼 케이스 : 추천, but CDN 방법에서는 안 먹힌다는걸 주의
컴포넌트 등록
- 전역으로 등록하는 방법
- createApp(App).component(’컴포넌트명’, 컴포넌트모듈)
- 지역등록 과정을 거치지 않고 바로 쓸 수 있다.
- 지역적으로 등록하는 방법(일반적)
- 쓰고 싶은 컴포넌트에서 import하고, components 속성으로 등록!
props
- html에서는 케밥케이스, 컴포넌트 내에서는 카멜케이스
- 컴포넌트의 props 속성으로 받는다
<template>{{message}}</template> <script> export default { props: ['message'] } </script>
v-bind
: 속성이 아닌 v-bind 속성 값으로 속성들이 담긴 객체를 할당해서 props를 한번에 줄 수 있다
const user = { name: "aj", age: 12, hobby: "reading a book" } <Hello v-bind="user" /> //v-bind:name, :age, :hobby 한번에
- props는 받는 컴포넌트 쪽에서에서는 변경불가하다.
- 해결방법1) 해당 props 값으로 가지는 data를 추가해서 그 data를 조작
- 해결방법2) 외부에서 이벤트 메서드를 처리, 하위 컴포넌트에서는 emit으로 이벤트를 발생시키는 방법
<template>{{message}}</template> <script> export default { props: ['message'], data() { return { msg: this.message } } } </script>
- props의 타입을 지정할 수 있다.
- 지정된 타입 외의 props가 오면 경고가 뜬다
export default { props: { message: String, age: [String, Number], } }
- 특정 props가 들어오지 않을 때를 대비해 기본값을 지정할 수 있다
- required 속성이 없다면 들어오지 않았을 때 오류가 나진 않음
export default { props: { ... email: { type: String, default: "leon@abc.com", required: true } } }
non-props 속성
- 컴포넌트에 props를 넘겨준다면, 받는 컴포넌트에서 아무런 지정을 해주지 않아도 template 내 태그에 그대로 적용된다
- 그러나 받는 컴포넌트의 최상위 요소가 두개 이상이면, 적용되지 않는다(상속)
//App.vue <template> <Hello class="hello" style="font-size: 100px;" @click="msg += '!'" /> </template> //Hello.vue <template> <h1>Hello</h1> => h1에 class, style, onClick 속성이 붙는다 </template>
- 받는 컴포넌트에서 vue 내장 객체인
$attrs
로 props를 받아올 수 있다 $attrs
: props로 넘겨는 주는데 등록은 하지않는 속성들로 이루어진 객체(=non-props)
<template> <h1 :class="$attrs.class">Hello</h1> <h2 :style="$attrs.style">Hahah</h2> <h3 @click="$attrs.onClick">so What?</h3> </template> <script> export default { //props 속성 없음 } </script>
<template> <h1 v-bind="$attrs">Hello</h1> //class,style, @click <h2 :style="$attrs.style">Hahah</h2> .. </template>
- props로 받아온다면 $attrs와 에 해당되지 않고, 최상위요소가 한개일 때 묵시적으로 적용되지도 않는다
<template> <h1 :class="$attrs.class">Hello</h1> <h2>Hahah</h2> <h3 @click="$attrs.onClick">so What?</h3> </template> <script> export default { props: { style: Object } } </script>
- non-props를 쓰지 않으려면 컴포넌트의 속성 중 inheritAttrs를 false로 준다(기본값은 true)
<script> export default = { inheritAttrs: false } </script>
커스텀 이벤트
- eval로 커스텀 이벤트를 발생시키고, 해당 컴포넌트의 props로 핸들러를 등록한다
- props를 부모, 자식 서로 갱신시키는 양방향 갱신!!
//Hello.vue <template> <h1 @click="#emit('please')">hello</h1> //커스텀 이벤트 발생 </template //App.vue <template> <Hello @please="reverseMsg" /> //커스텀 이벤트 핸들러 </template>
emits
옵션을 통해서 커스텀 이벤트를 등록할 수 있다- 커스텀 이벤트 목록을 명시적으로 확인 가능
- 묵시적으로 넘어오는 native 이벤트와 이름과 같으면 덮어쓴다 ⇒ 명시적으로 써야 함
//App.vue <template> <Hello @click="msg += ?!">hello</h1> </template> //Hello.vue <template> <h1>안녕</h1> //App에서 click이벤트(네이티브)가 묵시적으로 넘어오지만 </template> <script> export default = { emits: ['click'] // 얘가 덮어씀(이제 $emit('click')으로 써야함) } </script>
- emits 옵션에서 반환값을 표시할 수 있고, 함수의 기능을 추가할 수 있다
//App.vue <template> <Hello @click="msg += ?" @please="msg+=!">hello</h1> </template> //Hello.vue <template> <h1 @click="$emit('click')">안녕</h1> <h2 @click="$emit('please', 10)">hello</h2> //두번째 인수로 number </template> <script> export default = { emits: { click: null, //App.vue의 click이벤트만 please: number => { //App.vue의 please이벤트+추가기능 if (number > 10) { return true } else { console.error('') return false } } } } </script>
v-model
라는 props를 넘겨주고,modelValue
라는 이름으로 props를 받아온 후update
이벤트를 발생시키면, 부모의 데이터를 변경할 수 있다 ⇒ 부모-자식 양방향 데이터v-model: 새이름
으로 넘겨주면, modelValue이름 대신에 새이름으로 받을 수 있다
//App.vue <template> <h1>{{ msg }}</h1> <Hello v-model="msg" /> //props 넘겨주기, emit으로 인해 msg가 변경됨 </template> //Hello.vue <template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" > // 1. update 이벤트 발생하고 modelValue와 연결 // 2. input의 value를 App으로 넘겨줌 -> App의 msg가 변경됨 (양방향 데이터) </template> <script> export default { props: { modelValue: { //props받기 type: String, default: '' } }, .. </script>
- v-model을 2개 이상 만들 수 있음
//App.vue <template> <h1>{{ msg }}</h1> <h1>{{ name }}</h1> <Hello v-model:message="msg" v-model:name="name" /> </template> //Hello.vue <template> <input :value="message" @input="$emit('update:message', $event.target.value)" > <input :value="name" @input="$emit('update:name', $event.target.value)" > </template> <script> export default { props: { message: { type: String, default: '' }, name: { type: String, default: '' } }, .. </script>
컴포넌트 Slots
- 컴포넌트를 호출할 때 content를 넣어주면, 해당 컴포넌트의 <slot> 태그에서 받을 수 있다
- slot의 content를 넣어줘서 기본값을 줄 수 있다
//App.vue <template> <Hello><h3>hihi</h3></Hello> //content를 넣어줌 => slot으로 <Hello /> //content 없음 </template> //Hello.vue <template> <slot>냉무</slot> //내용이 들어옴. 기본값: 냉무 <h1>{{ msg }}</h1> </template>
- 컴포넌트의 자식을 template 태그로 분리하고, 해당 template에 v-slot으로 이름을 부여해서 slot을 나눠 쓸 수 있다
v-slot:이름
==#이름
- 해당 컴포넌트의 slot에서 name 속성과 같은 v-slot이름을 매칭시킨다
- 이름이 없는 자식은 기본 slot 태그로 받아올 수 있다
//App.vue <template> <Hello> <template v-slot:hi> //첫번째 자식 <h3>hihi</h3> </template> <template #bye> //약어, 두번째 자식 <h3>byebye</h3> </template> <h3>인사</h3> </Hello> //Hello.vue <template> <slot name="hi">냉무</slot> //첫번째 자식 <h1>{{ msg }}</h1> <slot name="bye" /> //두번째 자식 <slot></slot> </template>
- 범위를 가지는 slot
- slot에 값을 부여할 수 있고, 해당 값을 다시 가져올 수 있다
//App.vue <template> <Hello> <template #default="slotProps"> //name 속성없는 default slot //slotProps.noname으로 123을 받아올 수 있음 <h3>{{ slotProps.noname }}</h3> </template> </Hello> </template> //Hello.vue <template> <slot :noname="123"> //속성이름이 noname, 값은 123 냉무 </slot> <h1>{{ msg }}</h1> </template>
- slot의 이름을 동적으로 제어할 수 있다
// App.vue <template> <Hello> <template #[slotName]> <h3>kkk</h3> </template> </Hello> </template> <script> ... data() { return { slotName: 'abc' } }, </scipt> //Hello.vue <template> <slot name="abc"> 냉무 </slot> </template>
동적 컴포넌트
<component :is=”데이터” />
: 컴포넌트 이름을 동적으로 할당//App.vue <template> <component :is="componentName" /> </template> .. data() { return { componentName: 'Hello' } },
- keep-alive로 component를 감싸주면, 컴포넌트를 캐싱해서 다른 이름으로 컴포넌트가 교체되어도 unmount되지 않는다
<keep-alive> <template> <component :is="componentName" /> </template> </keep-alive>
⇒ 자주 토글되는 경우에만 쓰는 것이 좋다.