2024. 7. 7. 01:20ㆍFE/Vue.js
1. ref()
import { ref } from 'vue'
const count = ref(0)
Composition API에서 반응형 상태를 선언하는 권장 방법이다.
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
ref()
의 인자로 전달되면 ref object로 wrapped되어 반환된다.
이때 .value
프로퍼티를 가진다.
가. 왜 씀?
ref()
를 사용하면 변수의 값이 변경될 때 Vue.js가 자동으로 이 변경을 감지하고 DOM을 적절하게 업데이트한다.
이것이 가능한 이유는 Vue가 컴포넌트를 처음 렌더링할 때부터, 모든 ref를 기억,추적하기 때문이다.
표준 JavaScript에서는 일반 변수의 접근이나 변형을 감지하는 방법이 없다.
하지만, getter와 setter 메서드를 사용하여 객체의 속성의 get 및 set 연산을 가로챌 수 있다.
.value
속성은 Vue에게 ref가 액세스되거나 변경되었을 때를 감지할 기회를 만든다.
내부적으로, Vue는 getter에서 추적을 수행하고, setter에서 트리거를 수행한다.
개념적으로, ref를 다음과 같은 객체라고 생각할 수 있다:
// 실제 구현이 아닌 유사 코드
const myRef = {
_value: 0,
get value() {
track()
return this._value
},
set value(newValue) {
this._value = newValue
trigger()
}
}
나. setup()
Html과 같은 템플릿에서 ref
에 접근하려면 컴포넌트의 setup()
함수에서 선언하고 반환해야 한다.
import { ref } from 'vue'
export default {
// `setup`은 Composition API 전용 특수 후크입니다.
setup() {
const count = ref(0)
// ref를 템플릿에 노출
return {
count
}
}
}
<div>{{ count }}</div>
템플릿에서는 ref
를 사용할 때 .value
를 추가할 필요가 없다.
편의상 ref
는 템플릿 내에서는 자동으로 언래핑되기 때문.
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
function increment() {
// JavaScript 에서 .value 는 필요합니다.
count.value++
}
// 함수를 노출하는 것도 잊지 마세요.
return {
count,
increment
}
}
}
<button @click="increment">
{{ count }}
</button>
함수도 가능.
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }}
</button>
</template>
SFC(Single File Component)에서 사용하는 방법이다.
다. Deep Reactivity
import { ref } from 'vue'
const obj = ref({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function mutateDeeply() {
// these will work as expected.
obj.value.nested.count++
obj.value.arr.push('baz')
}
깊게 중첩된 객체, 배열 또는 내장 자료구조에서도 변화를 잘 추적한다.
Deep Reactivity를 원하지 않는다면 shallowRef()
라는 것을 사용하면 된다. (비용 문제, 통합, 최적화)
또한 Non-primitive values는 reactive()
로 reactive proxies로 전환한다.
라. DOM Update Timing
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// 이제 DOM이 업데이트되었습니다.
}
reactive state를 변경하면 DOM이 자동으로 업데이트 된다.
하지만 이 작업이 동기적으로 일어나진 않는다.
하나의 컴포넌트는 tick
단위로 한 번만 업데이트 된다.
아무리 많이 상태가 변해도 하나의 tick
에선 한번만 업데이트 된다.
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// 이제 DOM이 업데이트되었습니다.
}
명시적으로 tick
을 나누기 위해서(= 하나의 tick
이 끝나길 기다리려면) nextTick()
을 사용한다.
(전역 API다.)
2. reactive()
반응 상태를 선언하는 또 다른 방법이다.
ref
와 달리 wrap하지 않고 object
그 자체로 반응형으로 만든다. (객체 타입만 가능한다.)
import { reactive } from 'vue'
const state = reactive({ count: 0 })
<!-- SFC -->
<button @click="state.count++">
{{ state.count }}
</button>
이렇게 생성된 반응형 객체는 JavaScript Proxy이며 일반 객체처럼 동작한다.
이제 Vue는 reactivity tracking and triggering을 위해서 해당 객체의 모든 property에 대한 변경 시도를 가로챌 수 있다.
ref
와 마찬가지로 깊게 중첩된 객체, 배열 또는 내장 자료구조에서도 변화를 추적한다.
가. 재정의 vs 원본
const raw = {}
const proxy = reactive(raw)
// 반응형으로 재정의 된 것은 원본과 같지 않습니다.
console.log(proxy === raw) // false
reactive()
의 반환 값은 원본 객체를 재정의한 Proxy다.
고로 둘은 서로 다른 객체다.
원본 객체에 대한 변화는 뷰가 추적하지 않는다.
재정의한 프록시만 사용하자.
// 객체를 reactive() 한 반환 값과 프록시는 동일합니다.
console.log(reactive(raw) === proxy) // true
// 프록시를 reactive()한 반환 값과 프록시는 동일합니다.
console.log(reactive(proxy) === proxy) // true
프록시에 대한 일관된 접근을 보장하기 위해, 원본 객체를 reactive()
한 프록시와 프록시를 reactive()
한 프록시는 동일한 프록시를 반환하도록 동작한다.
const proxy = reactive({})
const raw = {}
proxy.nested = raw
console.log(proxy.nested === raw) // false
위 규칙은 중첩된 객체에도 동일하게 적용된다.
나. reactive의 한계
그렇다면 Vue에서 reactive
보다 ref
를 사용하는 걸 더 추천하는 걸까?
reactive()
는 …
object
(객체 타입)만 가능하다.- 전체 객체를 대체할 수 없다.
- 구조 분해 할당에 친화적이지 않음.
// 2. 전체 객체를 대체할 수 없다.
let state = reactive({ count: 0 })
// the above reference ({ count: 0 }) is no longer being tracked
// (reactivity connection is lost!)
state = reactive({ count: 1 })
// chaged but nothing triggered
const state = reactive({ count: 0 })
// count is disconnected from state.count when destructured.
let { count } = state
// does not affect original state
count++
// the function receives a plain number and
// won't be able to track changes to state.count
// we have to pass the entire object in to retain reactivity
callSomeFunction(state.count)
'FE > Vue.js' 카테고리의 다른 글
[Vue.js] Class & Style 바인딩 (0) | 2024.07.07 |
---|---|
[Vue.js] Computed Properties (0) | 2024.07.07 |
[Vue.js] Directive & Modifiers (0) | 2024.07.07 |
[Vue.js] Template Syntax (0) | 2024.07.07 |
[Vue.js] Vue.js란? (0) | 2024.07.07 |