아래 예제에서 버튼을 누르면 어떤 일이 일어날까요?
<template>
<button @click="increment">
{{ foo.count }}
</button>
</template>
<script setup>
const foo = { count: 0 };
function increment() {
foo.count++;
}
</script>
버튼을 누르면 값이 1씩 증가되는 것을 예상할 수 있습니다. 그러나 실제로 버튼을 누르면, 값은 변함이 없습니다. foo.count의 값은 증가했지만, template이 변화를 감지하지 못했기 때문입니다. 변화를 감지하도록 foo를 반응하는 객체(reactive object)로 만들 수 있습니다.
Reactive()
reactive()를 사용하면, reactive object를 만들 수 있습니다. ractive()는 자바 객체타입(배열, 컬렉션 포함)만 reactive object로 만들 수 있습니다. string, number, boolean같은 primitive type에는 사용할 수 없습니다.
사용방법
<script setup>을 사용해서 다음과 같이 사용할 수 있습니다.
<template>
<button @click="increment">
{{ foo.count }}
</button>
</template>
<script setup>
import { reactive } from 'vue'
const foo = reactive({ count: 0 })
function increment() {
foo.count++
}
</script>
깊은 반응성
reactive()는 기본적으로 깊은 반응성을 갖습니다. 객체 안에 중첩해서 사용한 객체나 배열의 변화도 감지하게됩니다.
import { reactive } from 'vue'
const obj = reactive({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function muntateDeeply() {
// obj의 중첩 객체, 중첩 배열에 상태변화를 줍니다.
obj.nested.count++
obj.arr.push('baz')
}
Proxy
reactive()의 리턴값은 오리지널 객체의 Proxy 입니다. 이 Proxy는 오리지널 객체와는 다른 객체입니다.
const origin = {}
const proxy = reactive(origin)
console.log(proxy === origin) // false
오직 proxy만 반응성을 갖습니다. origin의 상태변화는 감지되지 않습니다. 그러므로, 뷰의 반응형 시스템을 사용할 때는 proxy 버전만을 사용하는 것을 추천합니다.
proxy를 일관되게 사용하도록 보장하기 위해, reactive()를 사용하면 항상 같은 proxy를 사용할 수 있습니다.
const origin = {}
const proxy = reactive(origin)
// 같은 객체에 reactive()를 사용하면, 같은 proxy를 리턴합니다.
console.log(reactive(origin) === proxy) // true
// proxy에 reactive()를 사용하면 같은 proxy를 리턴합니다.
console.log(reactive(proxy) === proxy) // true
이 룰은 중첩 객체에도 적용됩니다. 깊은 반응성에 의해, 중첩객체 또한 proxy가 됩니다.
const origin = {}
const proxy = reactive({})
proxy.nested = origin
console.log(proxy.nested === origin) // false
console.log(proxy.nested === reactive(origin) // true
reactive()의 한계
reactive() API는 두가지 한계가 있습니다.
1. 객체 타입만 사용 가능
오직 object, array, collection type(Map 이나 Set같은) 타입에만 사용할 수 있습니다. string, number, boolean 과 같은 primitive types에는 사용할 수 없습니다.
2. 꺼내서 사용하지 못 함
2-1. 속성값을 꺼내서 변수에 할당하면, 반응성을 잃게 됩니다.
const state = reactive({ count: 0 })
// 변수에 한 번 할당하면 n과 state.count는 연결이 끊어집니다.
let n = state.count
// state.count의 값은 변하지 않습니다.
n++
n++
n++
console.log(n); // 3
console.log(state.count); // 0
2-2. 구조분할 할당도 반응성을 잃습니다.
const state = reactive({ count: 0 })
// count를 구조분할 할당해도 state.count와는 연결이 끊김.
let { count } = state
// state에는 영향을 주지 못함
count++
console.log(conunt); // 1
console.log(state.count); // 0
2-3. 함수로 값을 넘겨주어도 반응하지 않습니다.
const state = reactive({ count:0 })
function plusCount(count){
count++
}
// 함수는 plain number를 받는다.
// state.count를 추적해서 값을 바꾸지 못한다.
plusCount(state.count)
console.log(state.count) // 0
ref()
reactive()에서 속성값을 꺼내서 사용할 때 반응성을 잃는 한계와, 원시타입(string, number...) 에는 사용할 수 없는 한계를 극복하기 위해서, 뷰는 ref() 함수를 제공합니다. ref()는 타겟을 ref 타입으로 감싸게 됩니다. 감싸진 타겟은 .value를 사용해서 꺼낼 수 있습니다. ref가 자동으로 타겟을 reactive()로 변경합니다. 때문에, ref를 사용한 타겟이 반응성을 갖게 됩니다.
사용방법
ref()는 다음과 같이 사용할 수 있습니다.
<template>
<button @click="increment">
{{ foo.count }}
</button>
</template>
<script setup>
import { ref } from 'vue'
const foo = { count: ref(0) }
function increment() {
foo.count.value++
}
</script>
.value
ref()로 값을 감싸면, 그 값은 ref타입으로 감싸지게(wrapping) 됩니다. 래핑된 값은 .value를 사용해서 접근할 수 있습니다.
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
이러게 래핑된 값을 사용하게 되면, reactive()를 사용할 때의 한계를 극복할 수 있습니다.
2-1. 변수에 할당해도 반응성을 잃지 않습니다.
<script setup>
import { ref } from "vue";
// 0을 ref 타입으로 변경합니다.
const state = { count: ref(0) }
// state.count를 n에 할당합니다.
const n = state.count
// .value 값을 변경합니다.
n.value++
n.value++
n.value++
// n과 state.count가 함께 변경됩니다.
console.log(n.value); // 3
console.log(state.count.value); // 3
2-2. 구조분할 할당도 반응형으로 사용할 수 있습니다.
<script setup>
import { ref } from "vue";
const state = { count: ref(0) }
const { count } = state
count.value++
console.log(conunt.value); // 1
console.log(state.count.value); // 1
2-3. 함수로 값을 넘겨서 사용할 수 있습니다.
<script setup>
import { ref } from "vue";
const state = { count:ref(0) }
function plusCount(count){
count.value++
}
plusCount(state.count)
console.log(state.count) // 1
<template>에서 사용시에, ref는 래핑이 벗겨지게(unwrapping) 됩니다.
<script>안에서 .value를 사용해 꺼내서 사용했던 것 과는 다르게, template안에서는 .value없이 바로 꺼내서 사용할 수 있습니다.
<template>
<button @click="increment">
{{ count }}
</button>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0)
function increment() {
count.value++
}
</script>
'front > vue' 카테고리의 다른 글
Attribute와 Property의 차이 (0) | 2022.04.21 |
---|---|
[vue3 공식문서 번역]Essentials.1.Creating an Appliction (0) | 2022.04.21 |
[vue] DOM 과 Virtual DOM (1) | 2022.04.20 |
DOM 소개 (0) | 2022.04.20 |
[JavaScript] Proxy 설명 (2) | 2022.04.18 |
댓글