JS F-/Vue

반응형 기초(컴포지션 API)

원2 2024. 5. 27. 18:30
728x90
반응형


반응형 상태 선언

ref()
컴포지션 API 에서 반응형 상태를 선언하는 권장 방법은 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


참고
https://ko.vuejs.org/guide/typescript/composition-api.html#typing-ref

싱글파일 컴포넌트 https://ko.vuejs.org/guide/scaling-up/sfc.html

<script setup>
setup을 따로 선언해서 하는것은 너무 장황하게 작성하는 방법 일 수 있기에
파일 컴포넌트(SFC) 를 사용해서 단순화 시킬 수 있다.

 

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">
    {{ count }}
  </button>
</template>



템플릿을 동일한 범위에서 선언된 js 함수로 생각하면 편하다

왜 Refs를 사용해야 하는가??
- 템플릿에서 ref 를 사용하고 나중에 ref 의 값을 변경하면, vue 는 자동으로 변경을 감지, DOM을 업데이트 함
- 컴포넌트가 처음 렌더링 될 때, Vue는 렌더링 과정에서 모든 ref를 추적하고, 변경되면 추적하는 컴포넌트를 재렌더링 트리거함
- js에는 일반 변수의 접근이나 변형을 감지하는 방법이 없지만, getter setter 메서드를 사용해서 객체의 속성의 get 및 set 연산을 가로챌 수 있음
- .value 속성은 ref 가 엑세스 되거나 변경되었을 때 감지할 기회를 줌, getter 에서 추적하고 setter 에서 트리거를 수행
ref 를 다음과 같은 객체라고 생각할 수 있음

// 실제 구현이 아닌 유사 코드
const myRef = {
  _value: 0,
  get value() {
    track()
    return this._value
  },
  set value(newValue) {
    this._value = newValue
    trigger()
  }
}



깊은 반응형
refs 는 깊게 중첩된 개체, 배열 또는 Map 과 같은 js 내장 데이터 구조를 포함하여 모든 값 유형을 보유할 수 있음
중첩된 객체나 배열을 변경하더라도 변경 사항이 감지될 것으로 예상 가능

import { ref } from 'vue'

const obj = ref({
  nested: { count: 0 },
  arr: ['foo', 'bar']
})

function mutateDeeply() {
  // 예상대로 작동합니다
  obj.value.nested.count++
  obj.value.arr.push('baz')
}



얕은 참조 - https://ko.vuejs.org/api/reactivity-advanced.html#shallowref
의 경우 반응성을 위해 .value 액세스만 추적, 큰 개체의 관찰 비용을 피하거나 외부라이브러리에서 내부 상태관리 시 성능 최적화

DOM 업데이트 타이밍
반응 상태 변경 시, DOM 자동 업데이트 그러나 DOM 업데이트는 동기적으로 적용되지 않음
대신 다음 업데이트 주기의 "다음 틱"까지 버퍼링해서 많은 상태 변경을 수행하던 각 컴포넌트가 한 번만 업데이트되도록 함

import { nextTick } from 'vue'

async function increment() {
  count.value++
  await nextTick()
  // 이제 DOM이 업데이트되었습니다.
}


nextTick() - https://ko.vuejs.org/api/general.html#nexttick

reactive() - 반응 상태를 선언하는 다른 방법, 내부 값을 특수 객체로 감싸는 ref 와 달리 reactive 는 객체 자체를 반응형으로 만듬

import { reactive } from 'vue'

const state = reactive({ctn: 0});

//template
<button @click="state.ctn++">눌러보아라 이것이 reactive 다 ㅋ {{ state.ctn }}</button>


반응형 개체는 js Proxies 이며 일반 개체처럼 작동, 차이점은 Vue 가 반응성 추적 및 트리거링을 위해 반응성 객체의 모든 속성에 대한 액세스 및 변형을 가로챌 수 있다는 것.

반응형 재정의 vs 원본 
유의 : reactive 의 반환 값은 원본 객체와 같지 않고 원본 객체를 재정의한 프록시라는 점 (react 느낌, ES6 문법 spread 느낌)
*프록시 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

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

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




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

console.log(proxy === reactive(raw)); // true
console.log(proxy === reactive(proxy)); // true



중첩된 객체에도 적용됨, 

const proxy = reactive({})

const raw = {}
proxy.nested = raw

console.log(proxy.nested === raw) // false



reactive() 의 제한사항
1. 제한된 값 유형 : 객체 유형 (객체, 배열 및 컬렉션 유형에만 작동 ex_Map,Set) String, number, boolean 기본 유형보유 불가
2. 전체 객체를 대체할 수 없음 : 속성 액세스를 통해 작동하므로 동일한 참조를 항상 유지해야함! 대체불가능 (replace)

let state = reactive({ count: 0 })

// 위 참조({ count: 0 })는 더 이상 추적되지 않습니다.
// (반응성 연결이 끊어졌습니다!)
state = reactive({ count: 1 })


3. 분해 할당에 친화적이지 않음 : 반응형 객체의 타입 속성을 지역 변수로 분해하거나, 그 속성을 함수에 전달할 때 반응성 연결이 끊어짐

const state = reactive({ count: 0 })

// count는 분해 할당 될 때 state.count에서 연결이 끊어집니다.
let { count } = state
// 원래 상태에 영향을 주지 않음
count++

// 함수는 일반 숫자를 수신하고
// state.count에 대한 변경 사항을 추적할 수 없습니다.
// 반응성을 유지하려면 전체 개체를 전달해야 합니다.
callSomeFunction(state.count)


이런 제한때문에 기본 API로 ref() 를 사용하는게 정신건강에 좋다


추가적인 Ref 언래핑 세부 사항
ref는 반응 객체의 속성으로 액세스되거나 변경될 때 자동으로 래핑 해제됨

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1


ref가 할당된 기존 속성에 새 ref를 할당하면 이전 ref는 대체됨

const otherCount = ref(2)

// 기존 ref는 이제 state.count에서 참조가 끊어집니다. 
state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1


* ref의 언래핑은 깊은 반응형 객체 내부에 중첩된 경우에만 발생, 얕은 반응형 객체의 속서으로 접근하는 경우에는 적용x

배열 및 컬렉션의 주의사항
반응형 객체와 달리 ref가 반응형 배열의 요소 또는 Map 과 같은 기본 컬렉션 유형으로 액세스 될 때 랩핑해제 수행x

const books = reactive([ref('Vue 3 Guide')])
// .value가 필요합니다
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// .value가 필요합니다
console.log(map.get('count').value)



템플릿에서 래핑 해제시 주의사항
ref 언래핑은 ref가 템플릿 렌더링 컨텍스트의 최상위 속성인 경우에만 적용
예에서 count 및 object는 최상위 속성이지만 object.id는 그렇지 않음

const count = ref(0)
const object = { id: ref(1) }
{{ count + 1 }} // 이 표현식은 작동
{{ object.id + 1 }} // 작동 안함 [object Object]1 로 렌더링 됨



이 문제를 해결하기 위해 id를 최상위 속성으로 분해해야함

//js
const { id } = object
//tmp
{{ id + 1 }} // result : 2


주의사항 ref 텍스트 보간의 최종 평가 값은 경우 래핑x

{{ object.id }} // result : 1



https://v3-docs.vuejs-korea.org/guide/essentials/reactivity-fundamentals.html

 

Vue.js

Vue.js - The Progressive JavaScript Framework

vuejs.org

 

 

 

 

 

 

728x90
반응형

'JS F- > Vue' 카테고리의 다른 글

리스트 렌더링  (0) 2024.05.28
조건부 렌더링  (0) 2024.05.28
클래스와 스타일 바인딩  (0) 2024.05.28
계산된 속성  (0) 2024.05.27
템플릿 문법  (0) 2024.05.27
API 스타일  (0) 2024.05.27