음악, 삶, 개발
Vue 3 - dropdown 메뉴 만들기 (최소 순살 코드) 본문
다른 사람들의 코드를 보면 막 css class 명, vue 에서 제공하는 특정 함수들이 막 섞여있어서
그런것들 다 배제하고 작성해야하는 최소한의 코드만을 사용했다.
잔 곁가지들을 다 걷어내고 완전한 순살 코드만 작성한것이다.
"click" 대신 "mousedown" 을 사용하였고,
vue 의 component 들과 송신하기위한 ref 외에는 lifecycle hook 등은 일체 사용하지않고 구현하였다.
dropdown 메뉴에서 중요한건 window event listener 가 메뉴가 열리면 생기고, 닫히면 소멸해야하는것이다.
Vue 에서 잠시 Svelte 로 넘어갔던 이유가 boilerplate 코드가 너무 많다고 느꼈기때문인데,
Vue 3 의 script setup 이 생기면서 거의 Svelte 수준에 코드양을 가질수있게 되었다.
<!-- dropdown.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const state = ref (false)
const header = ref <HTMLElement | null> (null)
function toggleListener (active: boolean): void {
active ? window.addEventListener ("mousedown", mouseDownOutside) :
window.removeEventListener ("mousedown", mouseDownOutside)
}
function mouseDownHeader (): void {
toggleListener (state.value = !state.value)
}
function mouseDownOutside (e: MouseEvent): void {
const isOutside = header.value && !header.value.contains(e.target as Node)
if (isOutside) toggleListener (state.value = false)
}
function mouseDownItem (): void {
toggleListener (state.value = false)
}
</script>
<template>
<button ref = "header" @mousedown.prevent = mouseDownHeader >
click
</button>
<ul v-if = state >
<li @mousedown.prevent = mouseDownItem >one</li>
<li @mousedown.prevent = mouseDownItem >two</li>
</ul>
</template>
위의 코드에서 약간의 살을 붙여서, 선택한 item 을 header 에서 text 로 나타나게끔 해볼수있다.
<script setup lang="ts">
import { ref } from 'vue'
const items = ["one", "two", "three", "four"]
const value = ref (0)
const state = ref (false)
const header = ref <HTMLElement | null> (null)
function toggleListener (active: boolean): void {
active ? window.addEventListener ("mousedown", mouseDownOutside) :
window.removeEventListener ("mousedown", mouseDownOutside)
}
function mouseDownHeader (): void {
toggleListener (state.value = !state.value)
}
function mouseDownOutside (e: MouseEvent): void {
const isOutside = header.value && !header.value.contains(e.target as Node)
if (isOutside) toggleListener (state.value = false)
}
function mouseDownItem (index: number): void {
value.value = index
toggleListener (state.value = false)
}
</script>
<template>
<button ref = "header" @mousedown.prevent = mouseDownHeader >
{{items [value]}}
</button>
<ul v-if = state >
<li v-for = "(item, i) in items" @mousedown.prevent = "() => mouseDownItem (i)" >{{item}}</li>
</ul>
</template>