객체의 데이터를 컴포넌트의 props로 전달하려고 할 때, 보통은 아래처럼 작성한다.
user: {
id: 1,
name: JaeungE
}
<UserComponent :id="user.id :name="user.name"></UserComponent>
하지만 객체의 모든 데이터를 props로 전달하고자 한다면, 아래와 같이 축약이 가능하다.
<UserComponent v-bind="user"></UserComponent>
prop 이름을 따로 정의하지 않고, 단지 v-bind 디렉티브에 객체를 넘긴다는 것에 유의하자.
Props는 top-down의 단방향 데이터 흐름을 가지며, 하위 컴포넌트에서 props를 수정할 수 없다.
따라서 props로 받은 데이터의 수정이 필요하다면, 아래처럼 컴포넌트 내부에서 별도의 data로 만든 뒤에 수정하는 패턴을 사용하거나, 커스텀 이벤트를 사용한다.
props: ['propValue'],
data() {
return {
value: this.propValue
}
}
props를 객체로 정의한 뒤 key는 props, value는 type을 지정할 수 있다.
<script>
export default {
props: {
message: String
}
};
</script>
props 하나의 type이 아닌 여러 type을 가져야 한다면 배열로 선언할 수 있다.
<script>
export default {
props: {
message: [String, Number]
}
};
</script>
또한, 아래처럼 type 부분을 객체로 정의하고 default 프로퍼티를 이용해서 기본값을 지정할 수 있다.
<script>
export default {
props: {
message: {
type: [String, Number],
default: 'Default Message'
}
}
};
</script>
이 외에도 required 프로퍼티로 반드시 전달되어야 하는 props를 지정할 수 있으며, 참조형 데이터의 기본값은 항상 함수 형태로 반환해야 한다.
<script>
export default {
props: {
message: {
type: [String, Number],
default: 'Default Message',
required: true
},
user: {
type: Object,
default: function() {
return {
id: 1,
name: JaeungE
}
}
}
}
};
</script>
만약 기본적인 type 체크가 아닌, 커스텀한 유효성 검사를 하고 싶다면 validator 프로퍼티에 함수를 정의해서 사용할 수 있다.
컴포넌트에 정의된 속성들 중 props로 정의되지 않은 속성들은 $attrs라는 내장 객체에 저장되며, 이 속성을 Non-Prop 속성이라고 한다.
Non-Prop 속성은 컴포넌트 내부에서 this.$attrs를 이용해 접근이 가능하며, 루트 노드가 하나인 경우 자동으로 해당 노드에 적용된다.
App
<template>
<MyComponent
class="title"
@click="title += '~~!'"
/>
</template>
MyComponent
<template>
<h1
:class="$attrs.class"
@click="$attrs.onClick"
>
<div>
content
</div>
</template>
만약 루트 노드가 하나일 때, Non-Prop 속성이 하위 컴포넌트로 적용되는 것을 원하지 않는다면 컴포넌트의 inheritAttrs 프로퍼티를 false로 설정하도록 한다.
<script>
export default {
inheritAttrs: false,
...
}
</script>
컴포넌트 내부에서 emits 프로퍼티를 이용해 커스텀 이벤트를 명시하는 것 외에도, 객체 리터럴을 이용해서 유효성 검사가 가능하다.
<template>
<div>
<button @click="$emit('plus')">
Count + 1
</button>
<button @click="$emit('minus', count)">
Count - 1
</button>
</div>
</template>
<script>
export default {
props: {
count: {
type: Number,
default: 0
}
},
emits: {
plus: null,
minus: (count) => {
if(count > 0) {
return true;
} else {
console.error('count is not less than 0');
return false;
}
}
},
};
</script>
컴포넌트에서 기본적으로 아래와 같이 props를 이용한 양방향 데이터 바인딩을 할 수 있다.
App
<template>
<h1>
{{ msg }}
</h1>
<Hello
:message="msg"
@update="updateMsg" />
</template>
<script>
import Hello from '~/components/Hello';
export default {
components: {
Hello
},
data() {
return {
msg: 'Hello Vue!!'
};
},
methods: {
updateMsg(value) {
this.msg = value;
}
}
};
</script>
Hello
<template>
<label>
<input
:value="message"
@input="$emit('update', $event.target.value)">
</label>
</template>
<script>
export default {
props: {
message: {
type: String,
default: ''
}
},
emits: ['update']
};
</script>
컴포넌트의 props로 v-model 디렉티브를 이용하면 간단하게 양방향 바인딩 구현이 가능하며, 별도의 이름을 정의하지 않으면 modelValue라는 props에 v-model에 지정한 데이터가 바인딩 된다.
이때 이벤트 이름은 eventName:modelValue 형태로 작성해야 한다는 점을 주의하도록 하자.
App
<template>
<h1>
{{ msg }}
</h1>
<Hello
v-model="msg" />
</template>
<script>
import Hello from '~/components/Hello';
export default {
components: {
Hello
},
data() {
return {
msg: 'Hello Vue!!'
};
}
};
</script>
Hello
<template>
<label>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)">
</label>
</template>
<script>
export default {
props: {
modelValue: {
type: String,
default: ''
}
},
emits: ['update:modelValue']
};
</script>
만약 기본으로 사용되는 modelValue의 네이밍을 변경하고 싶다면, 아래와 같이 v-model:propName 형태로 변경할 수 있다.
App
<template>
<h1>
{{ msg }}
</h1>
<Hello
v-model:message="msg" />
</template>
<script>
import Hello from '~/components/Hello';
export default {
components: {
Hello
},
data() {
return {
msg: 'Hello Vue!!'
};
}
};
</script>
Hello
<template>
<label>
<input
:value="message"
@input="$emit('update:message', $event.target.value)">
</label>
</template>
<script>
export default {
props: {
message: {
type: String,
default: ''
}
},
emits: ['update:message']
};
</script>
이 외에도 v-model을 반복적으로 사용해서 여러 개의 데이터를 양방향 데이터로 바인딩할 수 있다.
컴포넌트 태그의 content를 <slot> 태그를 이용해서 가져올 수 있으며, content가 없는 경우 <slot> 태그의 content에 기본값을 정의할 수 있고, 이 값을 Fallback이라고 한다.
또한 content의 내용이 다중 요소일 때, v-slot:<name> 디렉티브를 이용해서 <slot>태그의 name 속성을 통해 위치를 매핑할 수 있다.
v-slot은 #으로 축약이 가능하다.
App
<template>
<Hello>
<template #before>
<h1>is Before Content</h1>
</template>
<template #after>
<h1>is After Content</h1>
</template>
<template #end />
</Hello>
</template>
<script>
import Hello from '~/components/Hello';
export default {
components: {
Hello
}
};
</script>
Hello
<template>
<slot name="before" />
<h1>Hello</h1>
<slot name="after" />
<slot name="end">
<h1>is End</h1>
</slot>
</template>
컴포넌트에서 특정 요소를 찾고 싶을 때, 해당 요소에 ref 속성을 정의하고 this.$refs 객체를 이용해서 참조할 수 있다.
<template>
<h1 ref="title">
Title~!
</h1>
</template>
<script>
export default {
mounted() {
console.log(this.$refs.title);
}
};
</script>
만약 ref 속성이 HTML요소가 아닌 컴포넌트에 적용되어 있다면, 해당 컴포넌트의 참조를 얻어오게 된다.
App
<template>
<Hello ref="hello" />
</template>
<script>
import Hello from '~/components/Hello';
export default {
components: {
Hello
},
mounted() {
const hello = this.$refs.hello;
console.log(hello.$refs.title);
}
};
</script>
Hello
<template>
<h1 ref="title">
Title~!
</h1>
</template>
위의 코드를 실행하면 App 컴포넌트가 마운트 됐을 때, Hello 컴포넌트의 h1 요소를 콘솔에 출력한다.