script setup 은 싱글 파일 컴포넌트(Single-File-Components) 구조에서 'Composition API' 를 사용하기 위한 진입점 역할을 하며, script 와 비교 했을 때 몇몇 장점이 존재한다.
따라서 script setup의 문법 구조를 지켜야 하고, 그 중 헷갈렸던 두 개념을 정리해보려고 한다.
emit 옵션을 사용하기 위해 선언하는 Vue 내장 APIs로, 하위 컴포넌트에서 상위 컴포넌트로 이벤트를 전달할 때 명시하고 사용한다.
//button.vue 하위컴포넌트
<template>
<button @click="$emit('test-emit')"> Emit 버튼 </button>
</template>
<script setup>
defineEmits(["test-emit"]);
</script>
//index.vue 상위컴포넌트
<template>
<button @test-emit="checkEmit"/>
</template>
<script setup>
import button from '@/component/button.vue'
const checkEmit = () => {
console.log('emit 발생')
}
</script>
script setup 구문을 사용하는 컴포넌트 인스턴스는 script setup 내에 선언한 어떠한 값도 컴포넌트 밖으로 내보낼 수가 없다. 그 이유는 script setup 구문 내에 갇히기 때문이고, 그렇게 갇힌 프로퍼티들은 ref 나 $parent 체인을 통해 접근이 가능하다.
따라서, ref 나 $parent 체인 내부의 속성값을 가져오려면 defineExpose APIs 로 내보내려는 속성들을 명시해야 하며, defineExpose 를 통해 내보낸 프로퍼티들은 ref 키워드로 상위 컴포넌트에서 하위 컴포넌트의 속성값에 접근이 가능하다.
<template>
<modal ref="refModal">
<component :is="state.modal" :id="state.id" @close="closeModal" />
</modal>
</template>
<script>
import modal from '@/component/modal'
import Regist from '@/component/regist'
import Modify from '@/component/modify'
export default {
components: {
Regist,
Modify,
},
}
</script>
<script setup>
import { ref, reactive } from 'vue'
const refModal = ref(null)
const state = reactive({
modal: null,
})
const closeModal = () => {
refModal.value.close()
}
</script>
//modal.vue 하위컴포넌트
<template>
<div v-if="state.visible">
<slot></slot>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const state = reactive({
visible: false,
})
// 부모 컴포넌트에서 접근하기 위한 함수.
const open = () => {
state.visible = true
}
const close = () => {
state.visible = false
}
defineExpose({
close,
open,
})
</script>
프로퍼티 선언과 값을 할당한 이후에 defineExpose({}) 해줘야 한다. js의 코드 컴파일러는 top->down 형식으로 코드를 읽기 때문에 선언문 이전에 defineExpose를 하면 ref 로 프로퍼티를 호출할 때 undefined 에러를 만날 것이다.