예시를 통해 이해해보자!
<template>
<!-- 이중 중괄호 문법 사이에 표현식을 작성할 수 있다. -->
<h1>{{ msg + '??' }}</h1>
<h1>{{ msg.split('').reverse().join('') }}</h1>
<button @click="add">
ADD
</button>
<h1>{{ reverseMessage() }}</h1>
<h1>{{ reverseMessage() }}</h1>
<h1>{{ reverseMessage() }}</h1>
<h1>{{ reverseMessage() }}</h1>
</template>
<script>
export default {
data() {
return {
msg: 'Hello Computed!'
}
},
computed: {
// 1. Getter로 사용하는 경우
reversedMessage() {
return this.msg.split('').reverse().join('')
// 'Apple' => ['A','p','p','l','e']
// => ['e','l','p','p','A'] => 'elppA'
},
methods: {
reverseMessage() {
return this.msg.split('').reverse().join('')
},
add() {
// Getter로 사용하는 경우 읽기 전용(Readonly)이기 때문에
// 할당 연산자를 통해 값을 할당 하더라도 반응적으로 동작할 수 없다.
this.reversedMessage += '!?'
}
}
}
</script>
위의 template태그의 내용 처럼 여러번을 반복할 경우 메소드를 사용하면 나올 때 마다 연산을 해야한다. 만약 몇 십, 몇 백개가 연산을 하게 된다면 자연스럽게 과부하가 오기 때문에 이렇게 사용하면 안된다.
이것을 해결하기 위해 computed를 사용한다.
<template>
<!-- 이중 중괄호 문법 사이에 표현식을 작성할 수 있다. -->
<h1>{{ msg + '??' }}</h1>
<h1>{{ msg.split('').reverse().join('') }}</h1>
<!-- 여러 번 반복 시 메소드 대신 computed의 캐시 기능을 사용한다. -->
<button @click="add">
ADD
</button>
<h1>{{ reversedMessage }}</h1>
<h1>{{ reversedMessage }}</h1>
<h1>{{ reversedMessage }}</h1>
<h1>{{ reversedMessage }}</h1>
</template>
<script>
export default {
data() {
return {
// Getter, Setter: 값을 얻어내거나 값을 지정하는 용도로 사용할 수 있다.
msg: 'Hello Computed!'
}
},
computed: {
// Getter, Setter로 사용하는 경우, 위의 data()와 연결되어 있다.
reversedMessage: {
// Getter, 값을 얻어내는 용도
get() {
return this.msg.split('').reverse().join('')
// 'Apple' => ['A','p','p','l','e']
// => ['e','l','p','p','A'] => 'elppA'
},
// Setter, 값을 할당하는 용도
set(v) {
this.msg = v
// 1. 버튼을 누를 경우 아래의 add()가 실행된다.
// 2. reversedMessage에 '!?'가 + 되면서 v에 들어온다.
// 3. v가 msg 데이터에 할당이 된다.
// 4. 변경된 값이 get()으로 가서 새로 변경된 값을 return통해 반환하여
// 화면에 출력
}
}
},
methods: {
reverseMessage() {
return this.msg.split('').reverse().join('')
},
add() {
// Getter와 Setter 둘 다 사용하는 경우에서 이를 해결한다.
this.reversedMessage += '!?'
}
}
}
1번의 reverseMessage()
메소드를 reversedMessage
로 바꿔서 출력해 보았다.
computed를 사용하게 되면 나올 때 마다 연산해서 출력하지 않아(캐싱) 부담이 줄어 든다.
즉, 데이터를 최적화 하는 용도로 사용할 수 있으며 원본의 데이터를 손상시키지 않고 계산을 한다.
Watch는 감시하는 데이터들이 변경되는 것을 감지할 때 실행된다.
감시하고 싶은 데이터가 있다면 watch부분에 메소드처럼 만들어 메소드의 로직으로 그 데이터가 변경됐을때 어떤 내용을 실행할 것인지를 명시해주면 된다.
쉽게 말하자면 데이터들의 변경 사항들을 감시하는 옵션이다.
아래의 예시를 참고하자!
<template>
<h1 @click="changeMessage">
{{ msg }}
</h1>
<h1>{{ reversedMessage }}</h1>
</template>
<script>
export default {
data() {
return {
msg: 'Hello Watch?'
}
},
computed: {
reversedMessage() {
return this.msg.split('').reverse().join('')
}
},
watch: {
// 감시하는 데이터들이 변경되는 것을 감지할 때 실행된다.
// 데이터들의 변경사항들을 감시하는 옵션이다.
// 1. 매개변수 이용 X
msg() {
// 변경이 일어났을 때 console.log를 출력
console.log('msg: ', this.msg)
// 2. 매개변수를 이용 O
msg(v) {
console.log('msg: ', v)
},
// 계산된 데이터도 감시할 수 있다.
reversedMessage() {
console.log('reversedMessage: ', this.reversedMessage)
}
},
methods: {
// click시 msg를 'Good!'으로 변경시키는 메소드
changeMessage() {
this.msg = 'Good!'
}
}
}
</script>
:class
에 객체를 전달하여 클래스를 동적으로 전환할 수 있다.
<template>
<!-- class binding -->
<h1
:class="{ active: isActive }"
@click="activate">
Hello?!({{ isActive }})
</h1>
</template>
<script>
export default {
data() {
return {
isActive: false
}
},
methods: {
activate() {
this.isActive = true
}
}
}
</script>
<style scoped>
.active {
color: red;
font-weight: bold;
}
</style>
위의 예시에서 active
클래스의 존재가 데이터 속성 isActive
의 true, false에 의해 결정됨을 의미한다.
<div :class="classObject"></div>
data() {
return {
classObject: {
active: true,
'text-danger': false
}
}
}
<div :class="classObject"></div>
data() {
return {
isActive: true,
error: null
}
},
computed: {
classObject() {
return
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
배열은 :class
에 전달하여 클래스 목록을 적용할 수 있다.
<div :class="[activeClass, errorClass]"></div>
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}
}
위는 아래와 같이 렌더링 된다.
<div :class="active text-danger"></div>
:style
의 객체 구문은 매우 간단하며 JavaScript 객체라는 점을 제외하면 CSS와 거의 비슷하다.
CSS 속성 이름에는 camelCase 또는 kebab-case를 사용할 수 있다.
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data() {
return {
activeColor: 'red',
fontSize: 30
}
}
}
<div :style="styleObject"></div>
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
}
<template>
<h1
:style="{
color,
fontSize
}"
@click="changeStyle">
Hello?!
</h1>
</template>
<script>
export default {
data() {
return {
color: 'orange',
fontSize: '30px'
}
},
methods: {
changeStyle() {
this.color = 'red',
this.fontSize = '50px'
}
}
}
</script>
:style
에 대한 배열 구문은 같은 스타일의 엘리먼트에 여러 개의 스타일 객체를 사용할 수 있게 한다.
<div :style="[baseStyles, overridingStyles]"></div>
<template>
<h1
:style="[fontStyle, backgroundStyle]"
@click="changeStyle">
Hello?!
</h1>
</template>
<script>
export default {
data() {
return {
fontStyle: {
color: 'orange',
fontSize: '30px'
},
backgroundStyle: {
backgroundColor: 'black'
}
}
},
methods: {
changeStyle() {
this.fontStyle.color = 'red',
this.fontStyle.fontSize = '50px'
}
}
}
</script>