v-if
디렉티브는 조건에 따라 블록을 렌더링 할 때 사용한다. 블록은 디렉티브의 표현식이 true 값을 반환할 때만 렌더링 된다.
v-if
는 전환 비용이 높으며 런타임 시 조건이 변경되지 않는다면 사용하는 것이 좋다.
<h1 v-if="awesome">Vue is awesome!</h1>
v-else와 함께 “else 블록”을 추가하는 것도 가능하다.
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
다음은 v-if, v-else-if, v-else로 구성된 예시이다.
버튼을 눌렀을 때 true, false, count의 숫자 증가에 따른 조건의 예시이며 이해를 하는데 참고해 보자!
<template>
<button @click="handler">
Click me!
</button>
<h1 v-if="isShow">
Hello?!
</h1>
<h1 v-else-if="count > 3">
Count > 3
</h1>
<h1 v-else>
Good~
</h1>
</template>
<script>
export default {
data() {
return {
isShow: true,
count: 0
}
},
methods: {
handler() {
this.isShow = !this.isShow
this.count += 1
}
}
}
</script>
또 다른 예시로 template
를 사용했을 때 이다.
최상위 template
아래의 template
에 v-if
를 사용하게 되면 template
는 화면에 렌더링 되지 않는다!
<template>
<button @click="handler">
Click me!
</button>
<template v-if="isShow">
<h1>Title</h1>
<p>Paragraph1</p>
<p>Paragraph2</p>
</template>
</template>
<script>
export default {
data() {
return {
isShow: true,
count: 0
}
},
methods: {
handler() {
this.isShow = !this.isShow
this.count += 1
}
}
}
</script>
엘리먼트를 조건에 따라 표시하기 위한 또 다른 방법으로 v-show
디렉티브가 있다.
사용법은 v-if
와 거의 동일하다.
차이점은 v-show
가 있는 엘리먼트는 조건에 상관 없이 항상 렌더링 되고 DOM(html)에 남아있다는 점이다. v-show
는 단순히 엘리먼트에 display CSS 속성을 토글한다.
v-show
는 초기 렌더링 비용이 높으며 무언가를 자주 전환해야 할 때 사용하는 것이 좋다.
<h1 v-show="ok">안녕하세요!</h1>
다음의 예시는 버튼을 눌렀을 때 v-show
를 이용하여 display 속성의 값이 토글 되는 것을 확인할 수 있는 예제이다.
<template>
<button @click="handler">
Click me!
</button>
<h1 v-show="isShow">
Hello?!
</h1>
</template>
<script>
export default {
data() {
return {
isShow: false,
count: 0
}
},
methods: {
handler() {
this.isShow = !this.isShow
this.count += 1
}
}
}
</script>
v-for
디렉티브는 객체의 속성을 반복할 때 사용한다.
다음 예시는 fruits라는 배열을 이용한 예시이다.
숫자 데이터 필요 시(index) 반복될 item을 소괄호로 묶고 뒤에',' 를 적고 변수를 선언하여 사용할 수 있다.
<template>
<ul>
<li
v-for="(f, i) in fruits"
:key="f">
{{ f }}-{{ i + 1 }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
fruits: ['Apple', 'Banana', 'Cherry']
}
}
}
</script>
또 다른 예시이다.
v-for로 배열 출력 시 배열의 아이템들을 고유하게 구분할 수 있는 특정 속성이 있어야 한다.
이 예시에서는 npm i -D shortid
라는 각각의 id를 고유하게 만들어주는 패키지를 설치하여 진행했다.
또한, 버튼을 눌러 'Orange'를 배열에 추가시키는 메소드도 사용했다.
<template>
<button @click="handler">
Click me!
</button>
<ul>
<li
v-for="{id, name} in newFruits"
:key="id">
{{ name }}-{{ id }}
</li>
</ul>
</template>
<script>
import shortid from 'shortid'
export default {
data() {
return {
fruits: ['Apple', 'Banana', 'Cherry']
}
},
computed: {
newFruits() {
return this.fruits.map(fruit => ({
id: shortid.generate(),
name: fruit
}))
}
},
methods: {
handler() {
this.fruits.push('Orange')
}
}
}
</script>
래핑된 메소드는 다음과 같다.
콘솔을 열고 이전 예제의 items 배열로 변이 메소드를 호출하여 재생할 수 있다.
example1.items.push({ message: 'Baz' })
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
이렇게 하면 Vue가 기존 DOM을 버리고 전체 목록을 다시 렌더링 한다고 생각할 수 있다. 하지만 그렇지는 않다. Vue는 DOM 요소 재사용을 극대화하기 위해 몇가지 똑똑한 구현을 하므로 배열을 겹치는 객체가 포함된 다른 배열로 대체하여 효율적이다.
v-on
디렉티브는 @
로 줄여서 사용할 수 있고, DOM 이벤트를 듣고 트리거 될 때 JavaScript를 실행할 수 있다.
<div id="example-1">
<button @click="counter += 1">Add 1</button>
<p>위 버튼을 클릭한 횟수는 {{ counter }} 번 입니다.</p>
</div>
Vue.createApp({
data() {
return {
counter: 0
}
}
}).mouont('#example-1')
많은 이벤트 핸들러의 로직은 더 복잡할 것이므로, JavaScript를 v-on
속성 값으로 보관하는 것은 간단하지 않다. 이 때문에 v-on
이 호출하고자 하는 메소드의 이름을 받는 이유이다.
<div id="example-2">
<!-- `greet`는 메소드 이름으로 아래에 정의되어 있습니다 -->
<button @click="greet">Greet</button>
</div>
Vue.createApp({
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet(e) {
// 메소드 안에서 사용하는 'this'는 Vue 인스턴스를 가리킨다.
console.log('Hello' + this.name + '!')
// 'event'는 네이티브 DOM 이벤트
if(e) {
console.log(e.target.tagName)
}
}
}
}).mount('#example-2')
예시를 보자
첫 번째 예시에서는 메소드 호출 시 인수를 넣어주지 않으면 event객체가, 인수를 넣으면 인수가 매개변수로 받아지는 예시이다.
인수 뒤쪽에 ','를 적어주고 $event객체를 직접적으로 명시가 가능하다.
<template>
<button @click="handler">
Click 1
</button>
<button @click="handler('hi', $event)">
Click 2
</button>
</template>
<script>
export default {
methods: {
handler(msg, e) {
// 인수를 넣어주지 않으면 event객체가, 인수를 넣으면 인수가 매개변수로 받아진다.
console.log(msg)
console.log(e)
}
}
}
</script>
두 번째 예시는 ','를 통해 여러개의 함수나 메소드를 동시에 실행할 수 있는 것을 보여주는 예시이다. 이때 ()는 꼭 써줘야 정상적으로 동작한다.
<template>
<button @click="handlerA(), handlerB()">
Click me!
</button>
</template>
<script>
export default {
methods: {
handlerA() {
console.log('A')
},
handlerB() {
console.log('B')
}
}
}
</script>
이벤트 핸들러 내부에서 event.preventDefault()
또는 event.stopPropagation()
를 호출하는 것은 매우 보편적인 일이다.
메소드 내에서 쉽게 이 작업을 할 수 있지만, DOM 이벤트 세부 사항을 처리하는 대신 데이터 로직에 대한 메소드만 사용할 수 있으면 더 좋을 것이다.
이 문제를 해결하기 위해, Vue는 v-on
이벤트에 이벤트 수식어를 제공하며 수식어는 점으로 표시된 접미사 이다.
<!-- 클릭 이벤트 전파가 중단됩니다 -->
<a @click.stop="doThis"></a>
<!-- 제출 이벤트가 페이지를 다시 로드 하지 않습니다 -->
<form @submit.prevent="onSubmit"></form>
<!-- 수식어는 체이닝 가능합니다 -->
<a @click.stop.prevent="doThat"></a>
<!-- 단순히 수식어만 사용할 수 있습니다 -->
<form @submit.prevent></form>
<!-- 이벤트 리스너를 추가할 때 캡처모드를 사용합니다 -->
<!-- 즉, 내부 엘리먼트를 대상으로 하는 이벤트가 해당 엘리먼트에서 처리되기 전에 여기서 처리합니다. -->
<div @click.capture="doThis">...</div>
<!-- event.target이 엘리먼트 자체인 경우에만 트리거를 처리합니다 -->
<!-- 자식 엘리먼트에서는 안됩니다 -->
<div @:click.self="doThat">...</div>
<template>
<a
href="https://naver.com"
target="_blank"
@click.once.prevent="handler">
NAVER
</a>
</template>
<script>
export default {
methods: {
handler(e) {
console.log('ABC!')
}
}
}
</script>
<template>
<div
class="parent"
@click="handlerA"
>
<!-- <div
class="parent"
@click.capture="handlerA"
> -->
<div
class="child"
@click.stop="handlerB"></div>
<!-- <div
class="child"
@click="handlerB"></div> -->
</div>
</template>
<script>
export default {
methods: {
handlerA() {
console.log('A')
},
handlerB() {
console.log('B')
}
}
}
</script>
<style scoped lang="scss">
.parent {
width: 200px;
height: 100px;
background-color: royalblue;
margin: 10px;
padding: 10px;
.child {
width: 100px;
height: 100px;
background-color: orange;
}
}
</style>
<template>
<!-- <div
class="parent"
@click.self="handlerA"
> -->
<div
class="parent"
@wheel.passive="handler"
>
<div
class="child"></div>
</div>
</template>
<script>
export default {
methods: {
handler(e) {
for(let i = 0 ; i < 10000 ; i += 1)
console.log(e)
},
handlerA() {
console.log('A')
},
handlerB() {
console.log('B')
}
}
}
</script>
<style scoped lang="scss">
.parent {
width: 200px;
height: 100px;
background-color: royalblue;
margin: 10px;
padding: 10px;
overflow: auto;
.child {
width: 100px;
height: 2000px;
background-color: orange;
}
}
</style>
키보드 이벤트를 청취할 때, 종종 공통 키 코드를 확인해야 한다. Vue는 키 이벤트를 수신할 때v-on
에 대한 키 수식어를 추가할 수 있다.
<input @keyup.enter="submit">
다음 수식어들을 사용하여 해당 수식어 키가 눌러진 경우에만 마우스 또는 키보드 이벤트 리스너를 트리거 할 수 있다.
다음의 예시에서 간단히 사용해보았다.
<template>
<input
type="text"
@keydown.ctrl.shift.a="handler" />
<!-- @keydown.enter="handler" /> -->
</template>
<script>
export default {
methods: {
handler() {
console.log('!!')
}
}
}
</script>
v-model
디렉티브를(:) 사용하여 폼 input과 textarea 엘리먼트에 양방향 데이터 바인딩을 생성할 수 있다.
v-model
디렉티브를 사용하지 않은 단방향 데이터 바인딩이다.<template>
<h1>{{ msg }}</h1>
<input
type="text"
:value="msg" />
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!'
}
}
}
</script>
v-model
디렉티브를 이용하지 않은 양방향 데이터 바인딩이다.<template>
<h1>{{ msg }}</h1>
<input
type="text"
:value="msg"
@input="handler" />
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!'
}
},
methods: {
handler(e) {
// 입력된 데이터가 출력
console.log(e.target.value)
this.msg = e.target.value
}
}
}
</script>
v-model
디렉티브를 이용한 양방향 데이터 바인딩이다.<template>
<h1>{{ msg }}</h1>
<input
type="text"
v-model="msg" />
<h1>{{ checked }}</h1>
<input
type="checkbox"
v-model="checked" />
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!',
checked: false
}
}
}
</script>
이때 v-model
사용 시 한글을 작성하면 하나의 글자가 작성될 때 까지 나타나지 않는다.
한글을 사용하려면 위의 v-model="msg"
대신
:value="msg" @input="msg = $event.target.value"
을 사용해야 한다.
<!-- v-model.number="msg"
v-model.trim="msg" -->
<template>
<h1>{{ msg }}</h1>
<input
type="text"
v-model.lazy="msg"
/>
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!'
// msg: '123'
},
watch: {
msg() {
console.log(this.msg)
}
}
}
}
</script>