객체의 데이터를 컴포넌트의 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
요소를 콘솔에 출력한다.