Vue에서 동적 ref 사용하기
Vue2버전 사용!
vue에서 DOM을 다루기 위해서는 ref를 사용하는데, ref의 이름을 동적으로 사용하는 방법을 정리해본다.
예를 들어 메뉴바 아래 현재 선택된 메뉴 표시하는 디자인인 경우. 새로 만드는 게 아니라 기존에 되어 있는 것에서 수정을 하려다 보니 완벽히 마음에 들지는 않지만, 심각했던(?) ui 오류는 고칠 수 있었다.
예시 코드
코드펜 오랜만에 들어가봤더니 뷰 코드펜 생겨서 써 봤다. 뷰를 널리 이롭게 하는 데에 도움이 될 것 같다!
예시 코드에서는 일반 스테틱 ref와 동적 ref를 둘 다 사용해 보았다.
See the Pen vue-dynamic-ref by kabinny (@kabinny) on CodePen.
template
:ref=”menu.title”
메뉴는 배열에서 v-for로 반복하기 때문에 ref를 각각 다르게 넣어주기 위해 ref의 이름을 변수로 넣어주었다.
<div class="active-bar" ref="activeBar"></div>
active-bar에는 일반적인 방법으로 ref 이름을 달아주었다.
<template>
<div id="app">
<div class="menu-wrap">
<ul>
<li
v-for="menu in menuList"
:ref="menu.title"
@click="getActiveStyle(menu.path)">
{{ menu.title }}
<!-- 원래는 라우터 이동을 위한 링크 -->
<!-- <router-link to="menu.path">{{ menu.title }}</router-link> -->
</li>
</ul>
<div class="active-bar" ref="activeBar"></div>
</div>
</div>
</template>
script
getActiveStyle
selectedMenu는 현재 선택된 메뉴 이름이 무엇인지 가져온다.
⇒ 그 이름을 가진 요소를 $refs에서 찾는다.
⇒ 찾은 요소의 offsetLeft 값을 activeBar의 left 값으로,
offsetWidth 값을 width 값으로 넣는다.
watch
메뉴에 따라 보이는 컨텐트 내용이 달라지는데, 현재 서비스에서는 그에 따라 라우터 변경도 된다. 그렇기 때문에 라우터가 변경될 때 마다 다시 호출이 필요해서 watch가 라우트 변경을 감시한다.
<script>
export default {
data() {
return {
menuList: [{
title: 'one',
path: '/one',
}, {
title: 'twotwo',
path: '/twotwo',
}, {
title: 'threethreethree',
path: '/threethreethree',
}]
};
},
methods: {
async getActiveStyle(path = '/one') {
// 원래는 path를 파라미터로 받지 않고 url의 path를 가져옴
// const path = this.$route.path
const selectedMenu = path.split('/')[1]
await this.$nextTick()
const selectedEl = this.$refs[selectedMenu][0]
this.$refs.activeBar.style.left = `${selectedEl.offsetLeft}px`
this.$refs.activeBar.style.width = `${selectedEl.offsetWidth}px`
}
},
watch: {
'$route'(to, from) {
// 원래는 메뉴 누르면 라우트도 바뀌기 때문에 라우트가 바뀔 때마다 호출
this.getActiveStyle()
}
},
mounted() {
this.getActiveStyle()
},
};
</script>
style
스크립트에서 active-bar의 left값과 width값을 바꿔주기 때문에 position: absolute로 하고 left, width 값은 임시값을 넣었다.
active-bar이동할 때 부드러운 전환 효과를 위해 transition을 넣어준다.
<style lang="scss">
.menu-wrap {
position: relative;
border: 2px solid aliceblue;
ul {
display: flex;
margin: 0;
padding: 0;
list-style: none;
li {
padding: 10px 20px;
cursor: pointer;
}
}
.active-bar {
position: absolute;
bottom: 0;
left: 0;
height: 5px;
width: 10px;
background-color: rgba(salmon, 0.4);
transition: all 0.3s;
}
}
</style>
issue
$refs[이름][0].$el
무언가의 버전 때문인 것 같은데, refs에서 요소 가져올 때 잘 안돼서 콘솔에 찍어 보고 구글링 해보고 따라해서 해결되었다.
실제 작업했던 코드에서는 이렇게 해야 정상 작동 했다.
const selectedEl = this.$refs[selectedName][0].$el
뷰 버전: "vue": "^2.6.11"
코드펜은 아마 뷰2 최신 버전인데 이렇게 작성했을 경우 오히려 작동을 안했다.
$nextTick()
돔 요소의 크기나 위치를 가져올 때 문제였는데 돔이 다 완료되기 전에 사이즈를 가져오려고 하면 제대로 값을 가져올 수 없다. 그래서 찾아 보다가 $nextTick
이라는 것을 알게 되었다.
async 함수 내에서 await this.$nextTick()
을 사용하면 이후의 코드는 돔 업데이트가 완료되면 실행된다.
Leave a comment