이벤트 내보내기와 듣기 (Emiiting and Listening to Events)
컴포넌트는 커스텀 이벤트를 템플릿 표현식(v-on 핸들러 같은)으로 직접적으로 내보낼 수 있다. 내장된 $emit 함수를 사용해서:
<!-- MyComponent -->
<button @click="$emit('someEvent')">click me</button>
parent는 v-on을 사용해서 들을 수 있다:
<MyComponent @some-event="callback" />
.once 접근제어자는 컴포넌트 이벤트 리스너에도 지원된다:
<MyComponent @some-event.once="callback" />
컴포넌트와 props와 같이, 이벤트 이름은 자동적인 케이스 변형을 제공한다. camelCase로 이벤트를 내보내도, 부모에서 kebab-cased 리스너를 사용해서 들을 수 있다. props casing 처럼, 템플릿의 이벤트 리스너에는 kebab-case를 사용하는걸 추천한다.
팁 네이티브 돔 이벤트와 다르게, 컴포넌트가 내보내는 이벤트들은 거품이 아니다. 직접적인 자식 컴포넌트에 의해 내보내진 이벤트만 들을 수 있다. |
이벤트 아규먼트
이벤트와 특정한 값을 내보내는건 유용하다. 예를 들면, <BlogPost> 컴포넌트가 텍스트의 크기를 얼마만큼 키울지를 원할 수도 있다. 이 경우에, 값을 제공하기 위해 $emit에 부가적인 아규먼트로 넘길 수 있다:
<button @click="$emit('inceaseBy', 1)">
Increase by 1
</button>
부모에 있는 이벤트가 들을 때, 리스너로 이벤트 아규먼트에 접근하도록 해주는, 인라인 화살표 함수를 사용할 수 있다:
<MyButton @increase-by="(n) => count += n" />
이벤트 핸들러가 메소드라면:
<MyButton @increase-by="increaseCount" />
값은 메소드의 첫번째 파라미터로서 넘겨질 수 있다:
function increaseCount(n) {
count.value += n
}
팁 이벤트이름다음으로 $emit()에서 넘겨지는 모든 부가적인 아규먼트들은 리스너로 전달될것이다. 예를들어, $emit('foo', 1, 2, 3) 을 리스너 함수는 3개의 아규먼트를 전달 받을 것이다. |
내보낸 이벤트 선언하기
내보낸 이벤트는 defineEmits() 매크로를 통해서 컴포넌트에 명시적으로 선언할 수 있다.
<script setup>
const emit = defineEmits(['inFocus', 'submit'])
</script>
리턴된 emit 함수는 자바스크립트부분에서 이벤트를 내보낼 때 쓰인다.
<script setup>을 쓰지 않으면, 이벤트는 emits 옵션을 사용해서 선언될 수 있고, emit 함수는setup() 컨텍스트에서 내보내진다:
export default {
emits: ['inFocus', 'submit'],
setup(props, ctx) {
ctx.emit('submit')
}
}
emit 옵션은 또한 객체형 문법을 지원한다. 객체형 문법은 내보내는 이벤트의 페이로드의 런타임 유효성을 수행할 수 있게 해준다:
<script setup>
const emit = defineEmits({
submit(payload) {
// 유효성의 성공여부를 가르키기 위해
// true나 false를 리턴해야 함
}
})
</script>
<script setup> 과 타입스크립트를 사용하면, 순수한 타입 애노테이션을 사용해서 내보내는 이벤트를 선언할 수 있다:
<script setup lang='ts">
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: stirng): void
}>()
</script>
더 자세한건: Typing Component Emits
선택사항이지만, 컴포넌트가 어떻게 동작하는지 더 잘 설명하기 위해 내보내는 모든 이벤트를 정의하는 것을 추천한다. 이건 뷰가 fallthrough 어트리뷰트에서 알려진 리스너로 내보내도록 해준다.
팁 만약 네이티브 이벤트(click같은)이 emits 옵션으로 정의되면, 리스너는 더이상, native 클릭 이벤트에는 응답하지 않고 컴포넌트가 내보낸 클릭 이벤트만 들을 것이다. |
이벤트 유효성 검사
prop 타입 유효성검사와 유사하게, 내보내진 이벤트는 배열 문법 대신 객체 문법으로 정의되어 유효성검사 될 수도 있다.
유효성 검사에 더해서, 이벤트는 함수로 배정되는데, 그 함수는 이벤트가 유효한지 아닌지 가르키는 불린을 리턴하고 emit call을 넘기는 아규먼트를 받는다.
<script setup>
const emit = defineEmits({
// 유효성 검사를 안함
click: null,
// 유효성 submit 이벤트
submit: ({ email, apssword }) => {
if (email && password) {
return true;
} else {
console.warn('Invalid submit event payload!')
return false;
}
}
})
function submitForm(email, password) {
emit('submit', { email, password })
}
</script>
v-model 과 사용하기
커스텀 이벤트는 v-model과 같이 동작하는 커스텀한 인풋을 만들기 위해 사용될 수 있다. 이거 기억나냐:
<input v-model="searchText" />
이거랑 같다:
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
컴포넌트와 사용할때, v-model은 이걸 대신할 수 있다:
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>
이게 동작하긴 하지만, <input> 안에있는 컴포넌트는 반드시:
- value 어트리뷰트를 modelValue prop에 바인딩해야 함
- input에서 새로운 값을 update:modelValue 이벤트로 내보내야함
이게 동작하는 코드다:
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
이제 v-model은 이 컴포넌트와 완벽히 동작한다:
<CustomInput v-model="searchText" />
이 컴포넌트에서 v-model을 구현하는 다른 방법은 getter와 setter를 둘 다 가진 writable computed 프로퍼티를 사용하는 것이다. get메소드는 modelValue 프로퍼티를 리턴하고, set 메소드는 상응하는 이벤트를 내보내야 한다:
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defuneEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
<template>
<input v-model="value" />
</template>
v-model 아규먼트
기본적으로, 컴포넌트에있는 v-model은 update:modelValue를 이벤트로, modelValue를 prop으로 사용한다. v-model로 아규먼트를 넘겨서 이 이름을 수정할 수 있다:
<MyComponent v-model:title="bookTitle" />
이 경우, 자식 컴포넌트는 title prop이여야 하고, 부모 값으로 업데이트하는 이벤트로는 update:title을 내보내야 한다:
<!-- MyComponent.vue -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>
<template>
<input
type="text"
:value="title"
@input="$emit('update:title', %event.target.value)"
/>
</template>
다양한 v-model 바인딩
By leveraging the ability to target a particular prop and event as we learned before with v-model arguments,
이전에 배운 v-model 아규먼트와 같이 특정한 prop과 event를 대상으로 하는 기능을 레버리징하여, 여러개의 v-model 바인딩을 하나의 컴포넌트 인스턴스에 만들 수 있다.
각각의 v-model은 다른 prop에 동기화 된다. 컴포넌트에 추가적인 옵션의 추가 없이:
<UserName
v-model:first-name="firstName"
v-model:last-name="lastName"
/>
<script setup>
defineProps({
firstName: String,
lastName: String
})
defineEmits(['update:firstName', 'update:lastName'])
</script>
<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>
v-model 접근지시어 다루기
폼 input 바인딩에 대해 배울 때, 내장된 접근지시어 .trim, .number, .lazy 를 봤을거다. 하지만 이런 경우에서, 커스텀한 접근지시어가 필요할 떄가 있을거다.
.capitalize라는 커스텀한 접근지시어를 만들어보자. capitalize는 v-model 바인딩에 의해 제공되는 문자열의 첫번째 글자를 대문자로 한다.
<script setup>
const props = defineProps({
modelValue:String,
modelModifiers: { default: () => ({}) }
})
defineEmits(['update:modelValue'])
console.log(props.modelModifiers) // { capitalize: true }
</script>
<template>
<input
type="text"
:value="modelValue"
@input="$emit('ipdate:modelValue', $event.target.value)"
/>
</template>
컴포넌트의 modelModifiers prop은 capitalize를 담고 있으며, 값은 true입니다 - v-model 바인딩이 v-model.cpitalize="myText"이기 떄문이다.
이제 위에 prop set up을 가지고, 내보내는 값을 바꾸기위한 핸들러를 쓰고 modelModifiers 오브젝트 키를 체크할 수 있다. 아래 코드에서 input 이벤트를 실행하는 <input /> 엘리먼트의 문자열을 첫글자를 대문자로 할 수 있다.
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
const emit = defineEmits(['update:modelValue'])
function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input type="text" :value="modelValue" @input="emitValue" />
</template>
아규먼트와 접근지시어 둘 다 v-model 바인딩을 위해, 생선된 prop의 이름이 arg + "접근지시어"이다. 예를들면:
<MyComponent v-model:title.capitalize="myText">
이에 상응하는 선언은 이렇다:
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }
'front > vue' 카테고리의 다른 글
[vue3 공식문서 번역]Components.4.Fallthrough Attributes (0) | 2022.05.03 |
---|---|
[뷰3 공식문서 번역] 반응형 심화 Reactivity in Depth (0) | 2022.05.03 |
[vue3 공식문서 번역]Components.2.Props (0) | 2022.04.29 |
[vue3 공식문서 번역]Components.1.Registration (0) | 2022.04.28 |
[vue3 공식문서 번역]Essentials.13.Components Basics (0) | 2022.04.28 |
댓글