[Vue] 반응형(Reactivity) 기초

chaewon·2024년 4월 11일
0
post-thumbnail

✍🏻반응형(Reactivity)

1. Reactivity

  • JavaScript 객체에서 반응형 상태를 생성하기 위해서는 reactive()함수를 사용할 수 있습니다.
<template>
	<div>
		<button v-on:click="state.count++">Click {{ state.count }}</button>
	</div>
</template>
import { reactive } from "vue";
export default {
	setup() {
		const state = reactive({
			count: 0,
		});
		return {
			state,
		};
	},
};

  • 클릭하면 카운터 되는 걸 볼 수 있다.
  • state가 아닌 state.count로 출력하는 이유는 state객체가 가지고 있는 속성이기 때문에 .을 이용해 count를 접근해야 한다.

메서드로 빼보자

<template>
	<div>
		<button v-on:click="increment">Click {{ state.count }}</button>
	</div>
</template>
import { reactive } from "vue";
export default {
	setup() {
		const state = reactive({
			count: 0,
		});
		const increment = () => {
			state.count++;
		};
		return {
			state,
			increment,
		};
	},
};
  • 이렇게 메소드를 활용해서도 만들 수 있고, 그리고 좀 더 안으로 들어가도 동작이 잘 된다.
<template>
	<div>
		<button v-on:click="increment">Click {{ state.count }}</button>
		<br /><br />
		<button v-on:click="increment">Click {{ state.deep.count }}</button>
	</div>
</template>
import { reactive } from "vue";
export default {
	setup() {
		const state = reactive({
			count: 0,
			deep: {
				count: 0,
			},
		});
		const increment = () => {
			state.count++;
			state.deep.count++;
		};
		return {
			state,
			increment,
		};
	},
};

  • 동작을 확인 할 수 있다. 이렇게 reactive함수는 객체나 배열과 같은 레퍼런스 타입을 반응형 객체로 만들 수 있다.


2. 프리미티브 타입(스트링, 넘버, 불린) -> 반응형 데이터로 만들기

<template>
	<div>
		<p>{{ message }}</p>
		<!-- 반응형 동작 확인 -->
		<button v-on:click="addMessage">Click!</button>
	</div>
</template>
import { reactive } from "vue";
export default {
	setup() {
		// const는 상수기 때문에 let으로 변경
		let message = reactive("Hello Vue!");
		const addMessage = () => {
			message = message + "!";
		};
		return {
			message,
			addMessage,
		};
	},
};

  • 이 상태에서 클릭했을 때 반응형 동작을 하지 않는다. 이유는 리액티브 함수는 객체나 배열과 같이 래퍼런스 타입의 반응형 상태, 반응형 객체를 선언하는 함수이기 때문이다.

여기서 중요한 부분🚨
reactive함수 - 객체나 배열 레퍼런스 타입(Reference type = 참조타입)을 반응형으로 만듦
ref함수 - string, number, boolean 프리미티브 타입(Primitive type = 원시타입)을 반응형으로 만듦

콘솔로 찍어보면

import { reactive } from "vue";
export default {
	setup() {
		// const는 상수기 때문에 let으로 변경
		let message = reactive("Hello Vue!");
		const addMessage = () => {
			message = message + "!";
		};
      	console.log("message", message);
		console.log("message", typeof message);
		return {
			message,
			addMessage,
		};
	},
};

  • message가 string으로 보인다. JavaScript는 값 자체를 변경하기 때문이다. 템플릿의 message와 선언한 message의 값이 메모리에 둘다 저장되어야 하지만 값을 변경해버리기 때문에 당연한 결과다.

하지만 리액티브 함수로 프리미티브 타입을 다룰 수 있다. 이미 약속된 속성을 선언하고 그 안에 선언하면된다.

<template>
	<div>
		<p>{{ message.value }}</p>
		<!-- 반응형 동작 확인 -->
		<button v-on:click="addMessage">Click!</button>
	</div>
</template>
import { reactive } from "vue";
export default {
	setup() {
		// const는 상수기 때문에 let으로 변경
      	// 이렇게 약속된 속성을 사용하면 된다.
      	// 그리고 속성을 연결해줘야하니 .value가 붙는다
		let message = reactive({
			value: "Hello Vue!",
		});
		const addMessage = () => {
			message.value = message.value + "!";
		};
		console.log("message", message.value);
		console.log("message", typeof message.value);
		return {
			message,
			addMessage,
		};
	},
};
  • 이렇게 속성을 사용해서 프리미티브 타입을 다룰 수 있다. 하지만 결국 프리미 타입을 선언한게 아니라 객체를 선언한거다. 그래서 반응형 API에서는 프리미티브 타입을 선언할 수 있는 ref()함수를 제공한다.

ref()함수는 프리미티브 타입의 반응형 데이터를 만들 때 사용하는 함수다. reactive()함수는 객체타입에만 동작한다. 그래서 기본타입(number, string, boolean)을 반응형으로 만들고자 할 때 ref()메소드를 사용할 수 있다. ref()메소드는 변이가능한 객체를 반환하는데 이 객체 안에는 value라는 하나의 속성만 포함되어 있고 value값은 ref()메소드에서 매개변수로 받는 값을 갖고 있다. 이 객체는 내부의 value값에 대한 반응형 참조(reference) 역할을 한다. 그래서 ref()함수다.

import { ref } from "vue";
export default {
	setup() {
		const count = ref(0);
		console.log(count.value); //0

		count.value++;
		console.log(count.value); //1
		return {
			ref,
		};
	},
};




3. 배열 및 컬렉션의 참조 Unwarpping

  • 리액티브 객체 정의를 할 때 속성으로 ref()함수를 넣으면 어떻게 될까?
  • Object와 Array
import { reactive, ref } from "vue";
export default {
	setup() {
		//ref -> Object
		const count = ref(0);
		const state = reactive({
			count,
		});
		console.log(count.value);
		console.log("state.count", state.count);

		//ref -> Array
		const message = ref("hello");
		const arr = reactive([message]);
		console.log("arr[0]", arr[0].value);
		return {};
	},
};



4. 반응형 상태 구조 분해(Destructuring)

  • 큰 반응형 객체의 몇몇 속성을 사용하길 원할 때 원하는 속성을 얻기 위해서 ES6 구조 분해 할당을 사용하는 것은 매우 일반적이다. 하지만 그러한 구조 분해로 반응형을 잃어버리게 된다.
<template>
	<div>
		<p>author: {{ author }}</p>
		<p>title: {{ title }}</p>
	</div>
</template>
import { reactive } from "vue";
export default {
	setup() {
		const book = reactive({
			author: "Vue Team",
			year: "2020",
			title: "Vue 3 Giude",
			descriprtion: "당신은 읽습니다.",
			price: "무료",
		});

		const { author, title } = book;
		return { author, title, book };
	},
};

  • book에 커서를 올려두면 리액티브 객체여서 수정할 수 있는 아이콘이 보인다. (반응형이기 때문에 수정이 가능)

  • 하지만 author, title의 경우에는 수정 아이콘이 보이지 않는다. 이유는 구조분해 할당을 하게 되면 author, title 변수에 속성이 재할당 되었다고 보면된다. 그래서 반응성을 잃어버린 그냥 스트링이된다. console.log(typeof author);로 속성을 찍어보면

    스트링인걸 확인 할 수 있다. 반응성을 잃어버린거다. 구조분해 후 반응형 할당을 할 수 있는데 이것은 toReftoRefs다.
import { reactive } from "vue";
export default {
	setup() {
		const book = reactive({
			author: "Vue Team",
			year: "2020",
			title: "Vue 3 Giude",
			descriprtion: "당신은 읽습니다.",
			price: "무료",
		});

		const { author, title } = toRefs(book);
		return { author, title, book };
	},
};

  • 이렇게 toRefs를 사용하게되면 book에 있는 속성과 구조 분해된 상태가 서로 동기화가 된다. book에 있는 속성을 변경하면 구조분해 할당된 부분도 변경이된다. 그리고 구조분해 할당이 아닌 객체에서 하나만 가져오고 싶다면 toRef를 사용하면된다.
import { reactive, toRef } from "vue";
export default {
	setup() {
		const book = reactive({
			author: "Vue Team",
			year: "2020",
			title: "Vue 3 Giude",
			descriprtion: "당신은 읽습니다.",
			price: "무료",
		});

		const author = toRef(book, "author");
		const title = toRef(book, "title");

		return { author, title, book };
	},
};
  • 이렇게 반응형(reactive) 객체에서 속성을 가져오고 싶을때는 사용하면된다.


5. readonly를 이용하여 반응형 객체의 변경 방지

  • 때때로 반응형 객체의 변화를 추적하기 원하지만, 또한 특정 부분에서는 변화를 막기를 원하기도 한다. 예를들어 Provide/Inject로 주입된 반응형 객체를 갖고 있을 때, 우리는 그것이 주입된 곳에서는 해당 객체가 변이되는걸 막고자 할 것입니다. 이렇게 하려면 원래 객체에 대한 읽기 전용 프록시를 생성하면된다.
import { reactive, readonly } from "vue";

export default {
	setup() {
      	//원본의 반응형 객체
		const original = reactive({
			count: 0,
		});
      	//복사본
		const copy = readonly(original);
      	//이렇게 복사본을 변경하려고하면 경고가 뜬다.
      	copy.count++;
		original.count++;
		console.log(original.count);
		console.log(copy.count);
		return {
			original,
			copy,
		};
	},
};

  • 위와 같이 경고 메세지가 뜬다.


5. Reactivity Transform 실험적인 단계

  • ref와 함께 .value를 사용해야 하는 것은 Javascript의 언어 제약으로 인한 단점이다.(프리미티브 타입을 레퍼런스 타입으로 다루기 때문이다.) 그러나 compile-time transforms을 사용하면 적절한 위치에 .value를 자동으로 추가하여 개선할 수 있다.
profile
의문은 '삶의 수준'을 결정하고, 질문은 '삶 자체'를 바꾼다.

0개의 댓글