computed 속성
# 기본 예제
<div id="example">
<p>원본 메시지: "{{ message }}"</p>
<p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: "#example",
data: {
message: "안녕하세요 computed 공부!"
},
computed: {
// 계산된 getter
reversedMessage: function() {
// `this` 는 vm 인스턴스를 가리킵니다.
return this.message
.split("")
.reverse()
.join("");
}
</script>
결과 :
computed
옵션은 함수처럼 생겼다. 하지만 취급은 속성처럼 취급한다. computed
뜻 그대로 이미 계산된 것이다. 그렇기 때문에 접근시에도 example.reversedMessage 로 메세지 역순이 나온다.
일반 속성처럼 computed 속성에도 템플릿에서 데이터 바인딩 할 수 있다.
그리고 가장 중요한 것은 우리가 선언적으로(선언형 프로그래밍 방식에 따라서(아래 computed와 watch 비교에 추가 설명)) 의존 관계를 만들었다는 것이다.
# computed의 속성의 캐싱 vs 메소드
표현식에서 메소드를 호출하여 같은 결과를 얻을 수도 있다.
<p>뒤집힌 메시지: "{{ reversedMessageMethod() }}"</p>
methods: {
reversedMessageMethod: function() {
return this.message
.split("")
.reverse()
.join("");
}
}
computed 속성 대신 메소드와 같은 함수를 정의할 수도 있다. 최종 결과에 대해 두 가지 접근 방식은 서로 동일하다.
차이점은 computed 속성은 종속 대상을 따라 저장(캐싱)된다는 것 이다. 즉, methods
는 매번 함수를 계산을 하고computed
옵션은 캐싱된 결과를 바로 리턴하는 것이다.
# computed 속성의 setter 함수
computed 속성은 기본적으로 getter 함수만 가지고 있지만, 필요한 경우 setter 함수를 만들어 쓸 수 있다.
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
watch 속성
대부분의 경우 computed 속성이 더 적합하지만 사용자가 만든 감시자가 필요한 경우가 있다.
watch는 데이터 변경에 대한 응답으로 비동기시 또는 시간이 많이 소요되는 조작을 수행하려는 경우에 유용하게 사용된다.
시간이 오래걸리거나 비동기로 처리하게되면 값이 나중에 바뀌는데, 그 시간동안 사용자가 뭘할지는 알 수가 없다.
하지만, watch를 사용하면 값이 변경되는 순간 변경된 값을 체크해내서 값을 읽어들일 수 있다.
# 예제
<div id="watch-example">
<p>yes/no 질문을 물어보세요:
<input v-model="question" /></p>
<p>{{ answer }}</p>
</div>
// ...
<!-- 이미 Ajax 라이브러리의 풍부한 생태계와 범용 유틸리티 메소드 컬렉션이 있기 때문에, -->
<!-- Vue 코어는 다시 만들지 않아 작게 유지됩니다. -->
<!-- 이것은 이미 익숙한 것을 선택할 수 있는 자유를 줍니다. -->
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: "#watch-example",
data: {
question: "",
answer: "질문을 하기 전까지는 대답할 수 없습니다."
},
watch: {
// 질문이 변경될 때 마다 이 기능이 실행됩니다.
question: function(newQuestion) {
this.answer = "입력을 기다리는 중...";
this.getAnswer();
}
},
methods: {
// _.debounce는 lodash가 제공하는 기능으로
// 특히 시간이 많이 소요되는 작업을 실행할 수 있는 빈도를 제한합니다.
// 이 경우, 우리는 yesno.wtf/api 에 액세스 하는 빈도를 제한하고,
// 사용자가 ajax요청을 하기 전에 타이핑을 완전히 마칠 때까지 기다리길 바랍니다.
// _.debounce 함수(또는 이와 유사한 _.throttle)에 대한
// 자세한 내용을 보려면 https://lodash.com/docs#debounce 를 방문하세요.
getAnswer: _.debounce(
function() {
if (this.question.indexOf("?") === -1) {
this.answer = "질문에는 일반적으로 물음표가 포함 됩니다. ;-)";
return;
}
this.answer = "생각중...";
var vm = this;
axios
.get("https://yesno.wtf/api")
.then(function(response) {
vm.answer = _.capitalize(response.data.answer);
})
.catch(function(error) {
vm.answer = "에러! API 요청에 오류가 있습니다. " + error;
});
},
// 사용자가 입력을 기다리는 시간(밀리세컨드) 입니다.
500
)
}
});
// ...
결과 :
# computed 속성 vs watch 속성
특히 AngularJS를 사용하던 경우 watch
를 남용하는 경우가 있다. 하지만 명령적인 watch
콜백보다 계산된 속성을 사용하는 것이 더 좋다.
watch 속성은 감시할 데이터를 지정하고 그 데이터가 바뀌면 이런 함수를 실행하라는 방식으로 소프트웨어 공학에서 이야기하는 ‘명령형 프로그래밍’ 방식.
computed 속성은 계산해야 하는 목표 데이터를 정의하는 방식으로 소프트웨어 공학에서 이야기하는 ‘선언형 프로그래밍’ 방식.
*watch 속성 남용의 예
<div id="app">
<h1>{{ count }}</h1>
<br />
<h2>computed: {{ calculated }}</h2>
<h2>watch: {{ watchedCount }}</h2>
<button @click="count --">카운트 감소</button>
</div>
// ...
new Vue({
el: "#app",
data: {
count: 3,
watchedCount: 9
},
computed: {
calculated: function() {
return this.count * 3;
}
},
watch: {
count: function(newVal) {
this.watchedCount = newVal * 3;
}
}
});
// ...
결과는 watchedCount
와 calculated
가 완벽히 똑같은 값을 출력한다.
*명령형 프로그래밍 방식
<div id="demo">{{ fullName }}</div>
// ...
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
// ...
명령형이고 또 코드를 반복하는 것을 볼 수 있다.
*선언형 프로그래밍 방식
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: "#demo",
data: {
firstName: "Foo",
lastName: "Bar"
},
computed: {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
});
// ...
훨씬 깔끔하다. 일반적으로 선언형 프로그래밍이 명령형 프로그래밍보다 코드 반복이 적은 등 우수하다고 평가하는 경향이 있다.
# computed와 구별되는 watch 사례
computed
가 계산된 값을 출력할 때 쓰인다면, watch
는 어떤 조건이 되었을 때 함수를 실행시키기 위한 트리거로서 사용할 수 있다.
<div id="app3">
<h1>{{ count }}</h1>
<!--computed의 calculated안의 함수를 실행시키기 위해서는 calculated를 실제로 출력해야 한다.-->
<!-- {{ calculated }} -->
<br />
<button @click="count --">카운트 감소</button>
</div>
// ...
new Vue({
el: "#app3",
data: {
count: 3
},
computed: {
calculated: function() {
if (this.count === 2) {
alert("값이 2가 되었습니다.");
}
}
},
watch: {
count: function(newVal) {
if (newVal == 0) {
alert("값이 0이 되었습니다.");
this.count = 3;
}
}
}
});
// ...
결과는 버튼을 눌러서 count
가 0이 되는 순간 alert
창이 뜨고 count
가 3으로 리셋이 된다. 하지만 calculated를 출력하지 않았기 때문에 computed 속성의 alert
창은 나타나지 않는다.
만약 위의 calculated
안에 alert
창을 띄우고 싶다면 HTML 코드 안에 {{ calculated }}
를 넣어서 실제로 출력을 해야calculated
안에 정의된 함수가 실행된다.