reactive()
함수를 사용해 객체 또는 배영을 반응형으로 만들 수 있음.
import { reactive } from 'vue'
const state = reactive({ count : 0 })
반응형 객체는 Javascript Proxy 이며 일반 객체처럼 작동함.
일반 객체와 차이점은 Vue가 속성에 접근 및 반응형 객체의 변경사항을 감지할 수 있다는 것.
컴포넌트의 템플릿에서 반응형 상태를 사용하려면, 컴포넌트의 setup()
함수에서 반응형 상태를 선언하고 반환해야 함.
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({ count: 0 })
return {
state
}
}
}
<div>{{ state.count }}</div>
반응형 상태를 변경하는 함수를 같은 범위에서 선언하고 상태와 함께 메서드로 노출할 수 있음.
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({ count: 0 })
function increment() {
state.count++
}
// 함수를 반환하는 것을 잊지 마세요.
return {
state,
increment
}
}
}
<button @click="increment">
{{ state.count }}
</button>
<script setup>
setup()
훅을 통해 상태와 메서드를 수동으로 노출하는 것을 장황할 수 있음.
싱글 파일 컴포넌트(*vue
) 사용시, <script setup>
을 사용하면 복잡성을 크게 단순화 할 수 있음.
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
function increment() {
state.count++
}
</script>
<template>
<button @click="increment">
{{ state.count }}
</button>
</template>
컴포넌트의 <script setup>
에서 import 또는 최상위 레벨로 선언된 변수나 함수는 해당 템플릿에서 바로 사용할 수 있음.
반응 상태를 변경하면 DOM이 자동으로 업데이트 됨.
하지만 DOM 업데이트는 동기적으로 적용되지 않음.
대신 Vue는 업데이트 주기의 '다음 틱' 까지 버퍼링하여 얼마나 많은 상태 변경을 수행하든 각 컴포넌트가 한 번만 업데이트 되도록 함.
상태 변경 후, DOM 업데이트가 완료될 때까지 기다리려면 nextTick() 전역 API를 사용할 수 있음.
Vue는 기본적으로 반응형 상태를 내부 깊숙이 추적하므로, 중첩된 객체나 배영을 변경할 때에도 변경 사항이 감지됨.
import { reactive } from 'vue'
const obj = reactive({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function mutateDeeply() {
// 변경 사항이
obj.nested.count++
obj.arr.push('baz')
}
reactive()
의 반환 값은 원본 객체와 같지 않고 원본 객체를 재정의한 프록시(Proxy) 라는 점을 유의해야함.
const raw = {}
const proxy = reactive(raw)
// 반응형으로 재정의 된 것은 원본과 같지 않습니다.
console.log(proxy === raw) // false
여기서 프록시만 반응형임. 원본 객체를 변경해도 업데이트가 트리거 되지 않음.
따라서 객체를 Vue의 반응형 시스템으로 작업할 때 가장 좋은 방법은 상태를 재정의한 프록시만 사용하는 것임.
reactive()
API는 두 개의 제한 사항이 있음.
1. 객체, 배열 그리고 Map
이나 Set
과 같은 컬렉션 유형에만 작동함. string
, number
또는 boolean
과 같은 기본 유형에는 사용할 수 없음.
let state = reactive({ count: 0 })
// 위에서 참조한 {{ count: 0 }}은 더 이상 추적되지 않음. (반응형 연결이 끊어짐)
state = reactive({ count: 1 })
또한 반응형 객체의 속성을 로컬 변수에 할당하거나 분해 할당 또는 함수에 전달할 때 반응형 연결이 끊어짐을 의미함.
const state = reactive({ count: 0 })
// n은 state.count에서 연결이 끊긴 로컬 변수입니다.
let n = state.count
// 원본의 상태(state.count)에 영향을 미치지 않습니다.
n++
// 로컬 변수 count는 state.count로부터 연결이 끊깁니다.
let { count } = state
// 원본의 상태(state.count)에 영향을 미치지 않습니다.
count++
// 함수는 일반적인 숫자를 수신하며,
// state.count의 변경 사항을 감지할 수 없습니다.
callSomeFunction(state.count)
Vue는 reactive()
의 제한 사항을 해결하기 위해, 어떠한 유형의 데이터라도 반응형으로 재정의할 수 있는 ref()
함수를 제공함.
import { ref } from 'vue'
const count = ref(0)
ref()
는 받는 인자를 .value
속성을 포함하는 ref 객체에 래핑 후 반환함.
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
반응형 객체의 속성과 유사하게 ref의 .value
속성은 반응형임.
또한 객체 유형을 가지고 있는 경우, ref는 자동으로 .value
를 reactive()
로 변환함.
ref가 값으로 객체를 가지는 경우, 객체 전체를 반응형으로 대체할 수 있음.
const objectRef = ref({ count: 0 })
// 이것은 반응형으로 작동함
objectRef.value = { count :1 }
또한 반응형 상태로 함수에 전달되거나 분해 할당될 수 있음
const obj = {
foo: ref(1),
bar: ref(2)
}
// 함수가 ref를 전달
// .value 를 통해 값에 접근해야 하지만 반응형 연결 상태가 유지됨
callsomeFunction(obj,foo)
// 분해 할당했지만, 반응형 상태가 유지됨
const { foo, bar } = obj
즉, ref()
를 사용하면 모든 값에 대한 "참조"를 만들어 반응성을 잃지 않고 전달할 수 있음.
이 기능은 컴포저블 함수로 로직을 추출할 때 자주 사용되기 때문에 상당히 중요함.
최상위 속성의 ref를 템플릿에서 잡근하면 자동으로 "언래핑"되므로 .value
를 사용할 필요가 없음.
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }} <!-- .value가 필요하지 않습니다. -->
</button>
</template>
javaScript 언어적 제약으로 인해 ref를 .value
와 같이 사용해야 하는 단점이 있음. 그러나 컴파일 시 변환을 사용해 적절한 위치에 .value
를 자동으로 추가하여 개발간 편의성을 개선할 수 있음.
<script setup>
let count = $ref(0)
function increment() {
// .value 가 필요하지 않음
count ++
}
</script>
<template>
<button @click="increment">{{ count }}</button>
</template>
반응형 변환은 아직 실험 단계이며 최종적으로 확정되기 전에 변경 될 수 있음.