Vue 3.0 새로 등장한 Vue.js Composition API
기존 인스턴스 옵션 단위가 아닌, 특정 기능이나 논리의 단위로 코드를 그룹화 하는것이 목적인 함수 기반의 API이다.
그룹화된 로직을 여러 컴포넌트에서 재사용할 수 있도록 코드의 재사용성과 타입 추론(타입스크립트)이 크게 개선되었다고 한다.
setup 함수는 beforeCreate 이전에 setup이 호출된다. (조기화 진입점, 마운트되기 전에 항상 실행)
컴포지션 API를 사용하는 경우 beforeCreate, created 훅 대신 setup을 사용한다.
인스턴스가 생성되기 전의 시점이기 때문에 컴포넌트 인스턴스에 접근하는 기능을 사용할 수 없다.
그렇기 때문에 setup 컴포넌트 안에서 this는 methos, data, computed등의 선언들에 접근을 할 수 없다.
[composition api를 사용하지 않았을 때]
반응형데이터 : 값이 변경됨에 따라 이를 감지하고 종속된 작업(side effect)가 실행 된다.
setup 함수를 이용하여 데이터를 전달하는 상황일때 만일 반응성을 이용하지 않는다면?
어떠한 값의 변경이 일어났을 때 해당 데이터를 사용하고 있는 기능, 화면에서 변화를 감지하지 않기에 상태를 유지할 수가 없게 된다.
vue2에서는 mutable한 값은 data에서 관리해왔다.
<template>
<h1>Home</h1>
<p>My name is {{ name }} and my age is {{ age }}</p>
</template>
<script>
export default {
data(){
return{
name: 'bini',
age: 33
}
},
</script>
하나의 컴포넌트를 만들기 위해서 아래 로직처럼 data외에 값을 조작하는 methods와 종속된 대상의 캐싱값을 반환받으면 변경이 일어나는 computed등이 사용되어진다. 기존 이러한 사용 방식은 직관적이지 않고 재사용성을 떨어트리는 단점을 갖고있다.
<template>
<h1>Home</h1>
<p>{{ countUntil }}</p>
<button @click="increase">increase</button>
<button @click="decrease">decrease</button>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
computed: {
// 저장된 결과(캐싱)를 반환하므로 종속 대상의 변경이 일어나기 전까지 호출 되지 않는다.
countUntil() {
return this.count + "입니다";
},
},
methods: {
increase() {
++this.count;
},
decrease() {
--this.count;
},
},
};
</script>
<style></style>
위 코드처럼 하나의 기능임에도 불구하고 이곳 저곳에서 사용되어 지고 있는데, 만일 규모가 커지게 된다면 복잡한 로직에서 관리하기에 힘들어질 수 있다.
컴포지션 API에서는 2가지 유형(reactive, ref)의 변경 가능한 반응형 데이터를 만들 수 있다.
프록시 객체: https://doqtqu.tistory.com/346
[ref]
<template>
<div>
<input type="text" ref="target">
</div>
</template>
<script>
export default {
mounted(){
console.log('target', this.$refs.target);
}
}
</script>
기존 vue에서 refs,
setup안에서 사용이 아닌 일반적으로 ref를 사용할때는 this.$refs~ 를 사용한다.
당연한 얘기겠지만, 렌더링이 완료된 직 후 ref를 사용할 수 있기에 mounted() 함수안에서 사용한다.
컴포지션 API의 ref를 통해 객체를 생성할 수 있다.
refs는 원시 타입을 포함한 여러가지 데이터 타입을 수용할 수 있다.
- 템플릿안에서 ref는 value 속성에 대한 접근없이 값 자체를 사용한다.
<template>
<h1>Home</h1>
<p ref="p">My name is {{ name }} and my age is {{ age }}</p>
<button @click="handleClick">click me</button>
<button @click="age++">{{ age }}</button>
</template>
<script>
import { ref } from "@vue/reactivity";
export default {
setup() {
console.log(this); //undefined
const name = ref("bini");
const age = ref(30);
const p = ref(null);
const handleClick = () => {
name.value = "luigi";
age.value = 20;
};
return { name, age, handleClick, p };
},
};
</script>
원본 값은 ref.value 속성을 통해 접근할 수 있다. 값을 변경할 때도 value속성에 접근하여 조작해야한다.
reactive는 원본 수정 시 value없이 바로 속성 접근가능
[reactive]
- composition api를 통해서만 생성할 수 있다.
- 객체만 받는다.
- reactive로 생성한 프록시 객체는 원본 객체와 완전히 동일하다.
- 값에 접근할 때는 원본 객체에 접근하는 방식과 같다.
- 모두 deep 한 객체를 받는다. reactive로 생성한 프록시 객체는 원본 객체와 완전히 동일
<template>
<h2>Refs</h2>
<p>{{ ninjaOne.name }} - {{ ninjaOne.age }}</p>
<button @click="updateNinja">update ninja one</button>
<h2>Reactive</h2>
<p>{{ ninjaTwo.name }} - {{ ninjaTwo.age }}</p>
<button @click="updateWww">update ninja two</button>
</template>
<script>
import { ref, reactive } from "@vue/reactivity";
export default {
setup() {
const ninjaOne = ref({ name: "ninJa", age: 33 });
const ninjaTwo = reactive({ name: "www", age: 22 });
const updateNinja = () => {
ninjaOne.value.age = 40;
};
const updateWww = () => {
ninjaTwo.age = 40;
};
return { ninjaOne, updateNinja, ninjaTwo, updateWww };
},
};
</script>
값에 접근할 때 ninjaTwo.name으로 받을 수 있다.
reactive를 통해 생성된 객체는 모두 깊은 감지를 수행하기 때문에 객체가 중첩된 상황에서도 반응성 데이터를 쉽게 조작하고 처리할 수 있다.
toRefs()
만약에 state를 반복해서 사용하는 상황일 경우?
애초에 보낼 때 반응성을 가진 데이터를 속성값을 써서 보내면 된다고 생각할 수 있다.
하지만 return에 속성 값을 직접 입력하게 된다면 반응성을 잃게 된다. 이런 상황에선 toRefs를 이용할 수 있다.
<template>
<p>{{ username.name }}</p>
</template>
<script>
import { reactive } from "@vue/reactivity";
export default {
setup() {
const username = reactive({
name: "ninja",
age: 3,
});
return { username };
},
};
</script>
- username.name을 여러개 사용해야 한다면?
<template>
<p>{{ username }}</p>
</template>
<script>
import { reactive } from "@vue/reactivity";
export default {
setup() {
const username = reactive({
name: "ninja",
age: 3,
});
return { username.name };
},
};
</script>
- username.name을 직접 return 시키면 되지 않나?
정답은 No! 직접 속성값을 전달하게 되면 반응성을 잃게 된다.
(상태 변화에 대한 감지가 불가능하게 된다는 말)
<template>
<p>{{ name }}</p>
<p>{{ age }}</p>
</template>
<script>
import { reactive, toRefs } from "@vue/reactivity";
export default {
props: ["post"],
setup() {
const username = reactive({
name: "ninja",
age: 3,
});
return toRefs(username);
},
};
</script>
반응성 주입시켜보자
<template>
<p>{{ isNotUgly }}</p>
</template>
<script>
import { ref } from "@vue/reactivity";
export default {
setup() {
const isHandsome = false;
const isNotUgly = isHandsome;
isHandsome = true;
return { isNotUgly };
},
};
</script>
일반적인 javascript 형태일 경우의 형태로 입력을 했을 때 isHansome의 값이 바뀌어도 isNotUgly값은 바뀌지 않는다.
vue에서는 isNotUgly값 또한 변화를 기대하기에 반응성을 주입을 시켜줘야한다.
<template>
<p>{{ isNotUgly }}</p>
</template>
<script>
import { ref } from "@vue/reactivity";
export default {
setup() {
const isHandsome = ref(false);
const isNotUgly = isHandsome;
isHandsome.value = true;
return { isNotUgly };
},
};
</script>
단일의 값인 경우는 ref를 이용할 수 있다.
더 다양한 데이터는 reative를 사용해서 보낼 수 있다.
<template>
<p>{{ isHandsome }}</p>
<p>{{ username.name }}</p>
</template>
<script>
import { reactive, ref, toRefs } from "@vue/reactivity";
export default {
setup() {
const isHandsome = ref(false);
const username = reactive({
name: "ninja",
age: 3,
});
return { isHandsome, username };
},
};
</script>
toRefs()
<template>
<p>{{ name }}</p>
</template>
<script>
import { reactive, toRefs } from "@vue/reactivity";
export default {
setup() {
const username = reactive({
name: "ninja",
age: 3,
});
return { ...toRefs(username) };
},
};
</script>
object형태로 값을 보내기때문에 스프레드 연산자를 이용해서 전달해야한다.
<template>
<div>{{ username }}</div>
<input type="text" v-model="username" />
<button @click="changeName">이름 변경</button>
<div>제품명: {{ name }}, 가격: {{ price }}</div>
<button @click="changeProduct">제품 변경</button>
</template>
<script>
import { reactive, ref, toRefs } from "@vue/reactivity";
export default {
setup() {
const username = ref("scalp");
function changeName() {
username.value = "messi";
}
const productList = reactive({
name: "tv",
price: 1000,
});
function changeProduct() {
(productList.name = "세탁기"), (productList.price = 5000);
}
return {
username,
changeName,
...toRefs(productList),
changeProduct,
};
},
};
</script>
refs와 reactive 차이점?
1. 타입 제한
ref는 string, number, object 등 어떠한 타입형태도 사용이 가능하다 .
reactive는 array, object, set, map 과 같은 타입에서만 사용할 수 있다.
(reactive는 Primitive Type(number, string, boolean ...)반응형 상태를 선언시 반응성이 유지되지 않는다.)
2. 접근 방식
ref는 value 속성을 접근할 수 있다.
reactive는 value속성 접근없이 반응성으로 사용한다.
3. 장점
ref는 타입의 제한이 없고, 단일 속성으로 사용이 가능하다.
reactive는 반응성 변수가 많이 선언할 때 간단하게 사용이 가능해진다.
참고블로그: https://geundung.dev/102
'Framework > vue_Architecture' 카테고리의 다른 글
Composition API: ref() vs reactive() (0) | 2023.01.02 |
---|---|
Vue Router(1) (0) | 2023.01.02 |
fetching data (vue.js) (0) | 2022.12.26 |
부모, 자식간 데이터 전송 (vue.js) (0) | 2022.12.25 |
데이터 양방향 바인딩 (vue.js) (0) | 2022.12.25 |
댓글