Declaring Reactive State
Options API에서는 data 옵션을 사용하여 component의 반응 상태를 정의하며 옵션 값은 객체를 반환하는 함수여야 한다. Vue는 새 component 인스턴스를 만들 때 함수를 호출하고 반응형 시스템으로 객체를 감싸 component 인스턴스에 $data로 저장한다. 편의상 최상위 수준의 속성도 component 인스턴스를 통해 바로 노출된다.
export default {
data() {
return {
count: 1
}
},
// `mounted`는 추후에 설명될 lifecycle hook
mounted() {
// `this`는 component 인스턴스를 나타냄
console.log(this.count) // => 1
// 데이터도 변경될 수 있음
this.count = 2
}
}
이러한 인스턴스 속성은 인스턴스가 처음 생성될 때만 추가되므로 data 함수에서 반환된 객체에 모든 속성이 존재하는지 확인해야 한다. 당장 원하는 값을 사용할 수 없는 속성의 경우 필요시 null, undefined, 다른 지정 값을 사용하면 된다.
data에 포함하지 않고 this에 직접 새 속성을 추가할 수 있지만 이런 방식으로 추가된 속성은 $data 객체로 처리되지 않기 때문에 Vue의 반응형 시스템에 의해 자동으로 추적되지 않는다.
Vue는 component 인스턴스를 통해 자체 내장 API를 호출할 때 접두사 $를 사용하며 내부 속성 정의를 위해 접두사 _를 사용한다. 그렇기에 최상위 수준의 data 속성에 해당 문자들로 시작하는 이름을 사용해선 안된다.
Reactive Proxy vs. Original
Vue3에서는 JavaScript 프록시를 활용하여 데이터를 반응형으로 만든다. Vue 2를 사용했었을 경우 아래와 같은 케이스를 주의하여야 한다.
export default {
data() {
return {
someObject: {}
}
},
mounted() {
const newObject = {}
this.someObject = newObject
console.log(newObject === this.someObject) // false
}
}
newObject를 this.someObject에 할당한 후 접근하면 Vue 2와 달리 기존 newObject는 그대로 유지되며 반응형으로 만들어지지 않는다. 항상 this를 이용하여 반응형 상태에 접근해야 한다.
Declaring Methods
Component 인스턴스에 메서드를 추가하려면 methods 옵션을 사용해야 하며 methods 옵션은 메서드를 포함하는 객체의 구조를 가진다.
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
// 메서드는 lifecycle hook 또는 다른 메서드에서 호출될 수 있다
this.increment()
}
}
Vue는 methods 안에서 component 인스턴스를 항상 참조할 수 있도록 this 값을 자동으로 바인딩한다. 이렇게 하면 메서드가 이벤트 리스너나 콜백으로 사용될 때, 올바른 this 값을 유지하게 된다. 화살표 함수를 사용해서 methods를 정의하면 Vue가 적절한 this 값을 바인딩하지 못하기 때문에 methods를 정의할 때 화살표 함수를 사용하지 않아야 한다.
export default {
methods: {
increment: () => {
// 잘못된 예 ----> 화살표 사용 금지
}
}
}
Component 인스턴스의 다른 속성들과 같이 methods 또한 component 템플릿 내부에서 접근할 수 있다. Methods는 일반적으로 템플릿 내부에서 이벤트 리스너로 사용된다.
<button @click="increment">{{ count }}</button>
위의 예제에서 <button>을 클릭하면 increment 메서드가 호출된다.
DOM Update Timing
반응형 상태를 변경하면 DOM이 자동으로 업데이트되지만 DOM 업데이트는 비동기로 처리함을 유의해야 한다. Vue는 데이터 변경 시 버퍼에 담은 후 이벤트 루프 tick에서 DOM 업데이트를 수행하기에 상태 변경 횟수에 관계없이 각 component가 한 번만 업데이트된다.
상태 변경 후 DOM 업데이트가 완료될 때까지 기다리려면 nextTick()이라는 전역 API를 사용할 수 있다. nextTick()을 사용하면 DOM 업데이트 완료 후 nextTick() 안에 작성한 코드가 수행된다.
import { nextTick } from 'vue'
export default {
methods: {
increment() {
this.count++
nextTick(() => {
// 업데이트 된 DOM에 접근
})
}
}
}
Deep Reactivity
Vue에서 반응형 변화는 깊게 적용되기에 모든 중첩된 속성의 변화까지도 감지한다.
export default {
data() {
return {
obj: {
nested: { count: 0 },
arr: ['foo', 'bar']
}
}
},
methods: {
mutateDeeply() {
// these will work as expected.
this.obj.nested.count++
this.obj.arr.push('baz')
}
}
}
root 수준에서만 추적되는 얕은 반응성 객체(shallow reactive objects)를 생성하는 것도 가능하지만 일반적으로 사용되진 않는다.
Stateful Method
일부 케이스의 경우 메서드 함수를 동적으로 생성해야 할 수도 있다. 예를 들어 디바운스 된 이벤트 핸들러를 생성하는 것.
import { debounce } from 'lodash-es'
export default {
methods: {
// Lodash의 디바운싱 적용
click: debounce(function () {
// ... respond to click ...
}, 500)
}
}
그러나 이런 접근 방식은 재사용되는 component가 모두 동일한 디바운스 함수를 공유하기 때문에 잠재적인 문제가 발생할 수 있다. Component 인스턴스를 서로 독립적으로 유지하기 위해 다음과 같이 created lifecycle hook에 디바운스 함수를 추가할 수 있다.
export default {
created() {
// 각 인스턴스는 독립적인 디바운스된 핸들러 사본을 가짐
this.debouncedClick = _.debounce(this.click, 500)
},
unmounted() {
// Component가 제거되면 타이머 취소
this.debouncedClick.cancel()
},
methods: {
click() {
// ... respond to click ...
}
}
}
관련 Repo
참고자료
https://vuejs.org/guide/essentials/reactivity-fundamentals.html
'Development > Vue.js' 카테고리의 다른 글
[Vue.js] Document 따라하기 - Class and Style Bindings (0) | 2022.06.03 |
---|---|
[Vue.js] Document 따라하기 - Computed Properties (0) | 2022.05.30 |
[Vue.js] Document 따라하기 - Template Syntax (0) | 2022.05.24 |
[Vue.js] Document 따라하기 - Creating a Vue Application (2) | 2022.03.11 |
[Vue.js] Vite를 이용하여 프로젝트 생성하기 (0) | 2022.03.10 |