안녕하세요 TriplexLab입니다.
vue3에서 추가된 setup function을 이용한 composition API에 대해서 살펴 보겠습니다. [링크참고]
바로 아래 예시를 보면 될것 같습니다.
vue3에서 추가되었다고 하지만 vue2(^2.6.14)에서도 사용가능 합니다.
👉🏻setup()
먼저 composition API본격적으로 알아보기 전에 setup에 대해서 살펴보도록 하겠습니다.
setup함수 인자로는 props로 상위 컴포넌트에서 데이터를 받을수 있습니다.
그리고setup함수안에서 가공된 데이터를 리턴하면, 다른 함수(메소드)에서 접근이 가능합니다.
<!-- MyBook.vue -->
<template>
<div>{{ collectionName }}: {{ readersNumber }} {{ book.title }}</div>
<button @click="incresement">incresement</button>
</template>
<script>
import { ref, reactive } from "vue";
export default {
props: {
collectionName: String,
},
setup(props) {
const readersNumber = ref(0);
const book = reactive({ title: "Vue 3 Guide" });
const incresement = () => {
readersNumber.value += 1;
};
// expose to template
return {
readersNumber,
book,
incresement,
};
},
created(){
this.init()
},
method: { //setup함수안에서 가공된 데이터를 리턴하면, 다른 함수(메소드)에서 접근이 가능합니다.
init(){
console.log(this.book.title = 'tetet')
this.incresement()
console.log(this.readersNumber)
}
}
};
</script>
중요한 차이점은 setup 함수는 컴포넌트 인스턴스가 생성되기 전에 실행된다는 점이다. 즉 컴포넌트 인스턴스에 접근이 필요한 기능은 사용할 수 없다. 즉 setup 안에서 this를 통해 컴포넌트 객체의 data, computed, methods 에서 선언한 것들에는 접근이 불가능하다. 그리고 인스턴스가 생성된 직후에 호출되는 created 메소드에 매칭되는 라이프 사이클 훅도 존재하지 않는다.
👉🏻ref
ref는 단일 데이터만 담을때 사용할 용도입니다.(ref 안에서는 계산속성을 사용할 수 없음)
예를 들어서 한가지 string값이라든지, boolean값이라든지 등등만 넣어서 반응성을 가진 데이터로 만들고 싶을때 사용하는것입니다.
const count = ref(0)
const boolean = ref(false)
const string = ref('triplexlab')
다음과 같이 사용해서 변수에 할당된것을 확인 해보면
<template>
<button @click="count++">{{ count }}</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0) //단일데이터를 넣을 ref
// 템플릿 및 기타 옵션 API 후크에 노출
return {count}
},
mounted() {
console.log(this.count) // 0 👈🏻결과
}
}
</script>
👉🏻reactive
reactive는 object 데이터를 담을때 사용할 용도입니다.(reactive 에서는 계산속성 사용가능)
바로 아래 예시1)를 보시죠.
예시1)
<template>
{{ username }}
{{ age }}
</template>
<script>
import { reactive, toRefs } from 'vue'
export default {
setup(){
const state = reactive({
username: 'TriplxLab',
age: 20
});
// return { 다음과 같이 리턴하면 반응성 손실, 값 변경시 재렌더링 동작 안함!!
// username: state.username,
// age: state.age
// }
return toRefs(state)
}
}
</script>
👉🏻 toRefs의 특징은 다음과 같다.
- toRefs 는 구성 요소가 반응성을 잃지 않고, 반환된 객체를 분해 / 확산할 수 있도록 컴포지션 함수에서 반응 객체를 반환할 때 유용하다.
- toRefs 는 reactive 객체에 포함된 속성에 대한 참조만 생성한다. 특정 속성에 대한 참조를 만드려면 toRef를 사용해야 함
예시2)
reactive로 되어 있는값을 변경하는 함수를 만들어 보겠습니다.
<template>
<div>
{{ username }}
{{ age }}
<button @click="changeFun">변경</button>
<br/>
<input type="text" v-model="username">
</div>
</template>
<script>
import { toRefs, reactive } from 'vue';
export default {
name: 'App-22299e',
setup(){
const state = reactive({
username: 'TriplxLab',
age: 50
})
function changeFun() {
state.username = 'ddsf'
state.age = 30
}
return {...toRefs(state), changeFun}
}
}
</script>
composables(컴포저블)를 활용한 로직 분리 및 재사용
다음은 vue의 composables(컴포저블)를 활용해서 위에서 다룬 ref, reactive를 예시를 보여 드리겠습니다.
Vue 애플리케이션의 맥락에서 "컴포저블"은 Vue의 Composition API를 활용하여 상태 저장 로직 을 캡슐화하고 재사용하는 기능입니다.[링크참고]
예시1) useObjectState
// composables/useObjectState.js
import { reactive, toRefs } from 'vue';
const useObjectState = (initialValue = {}) => {
const text = 'useObjectState 인자로 넣는것은 Object로만 넣어야 합니다.'
if(initialValue.constructor !== Object) {
console.warn(text)
}
let stateObject = reactive(initialValue);
const setStateObject = (newState) => {
stateObject = newState(stateObject)
}
return [toRefs(stateObject), setStateObject];
};
export default useObjectState;
// src/App.vue
<template>
<div>
{{ username }}
{{ age }}
<br/>
<input type="text" v-model="username">
<br/>
<button @click="changeFu">변경 버튼</button>
</div>
</template>
<script>
import useObjectState from '@/composables/useObjectState.js';
export default {
name: 'App-22299e',
setup(){
const [stateObject, setStateObject] = useObjectState({
username: 'TriplxLab',
age: 30
});
return {...stateObject, setStateObject}
},
methods: {
changeFu() {
this.setStateObject(() => {
this.username = 'TriplxLab2!!!'
this.age = '20'
})
}
}
}
</script>
예시2) useReducer
// composables/useReducer.js
import { ref } from 'vue';
const useReducer = (initialArg, reducer) => {
let state = ref(initialArg);
const dispatch = (action) => {
state.value = reducer(state.value, action);
};
return [state, dispatch];
}
export default useReducer;
// src/App.vue
<template>
<div>
{{ state.count }}
<button @click="dispatch({ type: 'decrement', mode: {step: 1} })">
-
</button>
<button @click="dispatch({ type: 'increment', mode: {step: 1} })">
+
</button>
<input type="text" v-model="state.count" >
</div>
</template>
<script>
import useReducer from '@/composables/useReducer.js';
export default {
name: 'App-22299e',
setup(){
const initialState = { count: 0 }
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + action.mode.step }
case 'decrement':
return { count: state.count - action.mode.step }
default:
throw new Error('잘못된 타입 입니다!')
}
}
const [state, dispatch] = useReducer(initialState, reducer);
return {state, dispatch}
}
}
</script>