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