vue.js에서 computed 프로퍼티는 유용하게 사용된다.
하지만 프로젝트를 하게 됐을 때 computed와 watch 두 가지를 다 사용하고 있었는데, computed / watch 모두 반응형이라는 키워드와 관련이 있다고만 이해하고 있었고 정확한 차이점을 잘 알지 못하고 사용했다.
computed는 "반응형 getter"라고 할 수 있다.
<div id="example">
<p>원본 메시지: "{{ message }}"</p>
<p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: '안녕하세요'
},
computed: {
// 계산된 getter
reversedMessage: function () {
// `this` 는 vm 인스턴스를 가리킵니다.
return this.message.split('').reverse().join('')
}
}
})
위 코드의 결과는 아래와 같다.
원본 메시지: "안녕하세요"
역순으로 표시한 메시지: "요세하녕안"
console.log(vm.reversedMessage) // => '요세하녕안'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'
computed의 reversedMessage를 프로퍼티가 정의될 때 내부적으로 Object.defineProperty를 통해 정의 되고, 이 때 익명함수가 getter로 설정된다.
vm.reversedMessage의 값은 항상 vm.message의 값에 의존한 상태이다.
일반 속성처럼 computed에도 템플릿에서 데이터 바인딩을 할 수 있다. Vue는 vm.reversedMessage가 vm.message의 값의 변화를 보고 있다는 것을 알기 때문에 vm.message가 바뀔 때 vm.reversedMessage에 의존하는 바인딩을 모두 업데이트한다.
<p>뒤집힌 메시지: "{{ reversedMessage() }}"</p>
// 컴포넌트 내부
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
computed 프로퍼티 대신 메소드와 같은 함수를 정의할 수도 있다. 결과적으로 computed와 메소드의 결과는 같지만, computed 프로퍼티는 종속 대상을 따라 저장(캐싱)된다는 것이다.
computed는 속성이 종속된 대상의 값이 바뀔 때만 함수를 실행하고, 즉 message가 바뀌지 않으면 reversedMessage를 여러번 요청해도 계산하지 않고 이전에 계산되어 있던 결과를 반환한다.
이에 비해 메소드를 호출하면 렌더링을 다시 할 때마다 항상 함수를 실행한다.
이러한 특성이 생기게 된 것은 getter의 특성이라고 할 수 있다. (이 특성 때문에 메소드와 차이가 생기기도 한다.) 이 점 때문에 값이 변해도 캐싱때문에 변경된 값을 인지하지 못하는 단점이 생기기도 한다.
🤔 여기서 캐싱은 왜 필요할까?
만약 계산에 시간에 오래 걸리는 computed 프로퍼티인 A를 가지고 있다면, 이 속성을 계산하기 위해 많은 데이터와 계산을 해야한다. 그런데 A에 의존하는 다른 computed 속성값도 있을 수 있는데, 캐싱을 하지 않으면 A의 getter 함수를 필요 이상으로 더 실행하게 된다. 그래서 캐싱을 원하지 않는 경우 메소드를 사용하는 것이 효율적이다.