[vue3] 반응형 기초(ref, reactive)

송인호·2023년 8월 11일
0

vue3

목록 보기
5/15

반응형 상태 설정

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이 자동으로 업데이트 됨.
하지만 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')
}

반응형 재정의 vs 원본

reactive() 의 반환 값은 원본 객체와 같지 않고 원본 객체를 재정의한 프록시(Proxy) 라는 점을 유의해야함.

const raw = {}
const proxy = reactive(raw)

// 반응형으로 재정의 된 것은 원본과 같지 않습니다.
console.log(proxy === raw) // false

여기서 프록시만 반응형임. 원본 객체를 변경해도 업데이트가 트리거 되지 않음.
따라서 객체를 Vue의 반응형 시스템으로 작업할 때 가장 좋은 방법은 상태를 재정의한 프록시만 사용하는 것임.

reactive() 의 제한 사항

reactive() API는 두 개의 제한 사항이 있음.
1. 객체, 배열 그리고 Map이나 Set과 같은 컬렉션 유형에만 작동함. string, number 또는 boolean 과 같은 기본 유형에는 사용할 수 없음.

  1. Vue의 반응형 변경 감지는 속성에 접근함으로써 작동하여, 항상 반응형 객체에 대한 동일한 참조를 유지해야함. 즉, 첫 번째 참조에 대한 반응형 연결이 손실되기 떄문에 반응형 객체를 쉽게 "교체" 할 수 없음을 의미함.
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)

ref()를 사용한 반응형 변수

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는 자동으로 .valuereactive() 로 변환함.

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 언래핑

최상위 속성의 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>

반응형 변환은 아직 실험 단계이며 최종적으로 확정되기 전에 변경 될 수 있음.

profile
프론트엔드 개발자

0개의 댓글