computed와 watch 속성
computed
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{ num }}</p>
<p>{{ doubleNum }}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
num: 10
},
computed: {
doubleNum: function() {
return this.num * 2;
}
}
});
</script>
</body>
</html>
computed 속성 사용 시 데이터에 대한 의존성을 가진다. 그리고 기본적으로 getter 함수를 가지고 있다.
vm.doubleNum 값은 vm.num 값에 의존하게 된다. doubleNum에 대한 로직이 실행될 때 기준이 되는 값이 num인 것이다. 따라서 num 값이 변경될 때마다 doubleNum 값도 변경된다.
computed 속성 대신 methods 속성을 사용하여 doubleNum 값을 불러올 수도 있다.
...,
methods: {
doubleNum: function() {
return this.num * 2;
}
}
...
이는 결과 값을 불러오는 데에 있어서는 똑같다. 그러나 의존성이라는 것에 있어 차이가 있다.
computed 속성은 종속 대상을 따라 저장(캐싱)된다.
computed 속성을 이용하는 경우 doubleNum 메서드를 여러 번 호출하여도, num 값이 변경되지 않는 한 doubleNum은 계산되지 않는다.
그러나 methods 속성을 이용하면 doubleNum 메서드를 호출할 때마다 매번 실행한다. num 값이 변경되지 않은 상태에서 호출해도 계속 실행된다.
만약 계산에 시간이 오래 걸리는 데이터 A가 computed 속성이고, 데이터 A에 의존하는 또 다른 computed 속성 값 B가 있다고 가정하자. 이때 데이터 A를 캐싱(저장) 하지 않으면 단순 A 데이터가 필요할 때보다 메서드가 더 많이 실행되게 될 것이다.
데이터 A가 변경될 때마다 B를 변경하는 메서드도 실행될 테니 캐싱을 하지 않았다면 해당 메서드 실행 시 다시 A의 getter 함수를 실행시키게 되는 것이다.
만일 캐싱을 원하지 않는 경우에는 methods 속성을 사용하면 된다.
예제
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.warning {
color: red;
}
</style>
</head>
<body>
<div id="app">
<p v-bind:class="cname">Hello</p>
<p v-bind:class="{ warning: isError }">Hello</p>
<p v-bind:class="errorTextColor">Hello</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
cname: 'red-text',
isError: false
},
computed: {
errorTextColor: function() {
return this.isError ? 'warning' : null;
}
}
});
</script>
</body>
</html>
class 속성을 computed 속성을 사용하여 지정하는 예제이다.
첫 번째 <p> 태그처럼 단순히 v-bind 디렉티브만을 사용하여 클래스를 지정할 수 있다.
또한 두 번째 <p> 태그처럼 v-bind를 사용하여 { warning: isError }라는 식을 통해 isError 값이 true이면 warning이라는 class 값을 지정해줄 수 있다. 하지만 해당 방법보다는 computed 속성을 사용하는 것이 좀 더 직관적이다.
세 번째 <p> 태그에서는 isError 값의 의존하는 errorTextColor를 사용한다. errorTextColor 메서드에서는 삼항 연산자를 사용해 isError가 true인 경우 warning 값을 반환하고, false인 경우는 null을 반환한다.
watch
대부분의 경우 computed 속성이 더 적합하지만 사용자가 만든 감시자가 필요한 경우가 있다. watch 속성은 데이터 변경에 대한 응답으로 비동기식 또는 시간이 많이 소요되는 조작을 수행하려는 경우에 유용하다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
{{ num }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
num: 10
},
computed: {
doubleNum: function() {
return this.num * 2;
}
},
watch: {
num: function(newValue, oldValue) {
console.log('oldVal : ' + oldValue);
console.log('newVal : ' + newValue);
this.fetchUserByNumber(newValue);
}
},
methods: {
fetchUserByNumber: function(num) {
console.log(num);
}
}
})
</script>
</body>
</html>
watch 속성도 num 값이 변경되었을 때 fetchUserByNumber 메서드가 실행되기 때문에 computed 속성과 유사하다고 볼 수 있다.
watch 속성은 데이터의 변화를 추적하기 때문에 해당 데이터가 변경되기 전 값과 변경되고 나서의 값, 두 값 모두를 인자로 받을 수 있다. 변경된 후 값이 첫 번째 매개변수로 자리하고, 변경되기 전 값이 두 번째 매개변수로 자리한다. 대부분의 경우 computed 속성이 적합하지만 데이터 감시를 위해 watch 속성을 쓰는 경우도 있다.
computed vs watch
computed 속성은 단순한 값에 대한 계산에 있어 적합하다. validation 값을 계산 또는 간단한 텍스트를 넘기는 동작하는 것을 정의한다. 이와 달리 watch 속성은 매번 실행되기에는 부담스러운, (데이터 요청과 같은) 무거운 로직 실행에 있어 적합하다.
computed 속성은 계산해야 하는 목표 데이터를 정의하는 방식으로 선언현 프로그래밍 방식이라 할 수 있고, watch 속성은 감시 데이터를 지정하고 그 데이터가 바뀌면 이런 함수를 실행하라는 방식으로 명령형 프로그래밍 방식이라 할 수 있다.
[공식 문서] https://vuejs.org/v2/guide/computed.html
[인프런] Vue.js 시작하기 - Age of Vue.js