Vuex
기초 설정 및 사용
모든 컴포넌트들이 데이터를 공유하는 하나의 파일을 만드는 것입니다.
쓰임새도 생김새도 리덕스와 비슷한 듯 보입니다.
npm install vuex@next
store.js
import { createStore } from "vuex";
const store = createStore({
state() {
return {
likes: 0,
};
},
});
export default store;
main.js
import { createApp } from "vue";
import App from "./App.vue";
import store from "./store.js";
createApp(App).use(store).mount("#app");
데이터를 관리할 파일을 만들고 연결합니다.
App.vue
<template>
<div>{{ $store.state.likes }}</div>
<button @click="$store.state.likes++">클릭</button> <!--비추-->
</template>
...
다음과 같이 데이터를 편하게 꺼내 쓰고 변경할 수 도 있습니다.
하지만 이렇게 무차별적으로 데이터를 직접 변경하면 관리가 쉽지 않기에 한 곳에서 메서드를 정의하고 관리하는 게 좋습니다.
store.js
import { createStore } from "vuex";
const store = createStore({
state() {
return {
likes: 0,
};
},
// 여기
mutations: {
plusLike(state) {
state.likes++;
},
},
});
export default store;
이렇게 함수를 저장할 공간으로 mutations를 사용하고 다음과 같이 함수를 정의합니다.
여기서 this.likes이 아닌 매개변수로 state를 받아 state.likes로 연결해줘야 합니다.
App.vue
<template>
<div>{{ $store.state.likes }}</div>
<button @click="$store.commit('plusLike')">좋아요</button> <!--여기-->
</template>
...
다음과 같이 메서드의 이름을 이벤트와 연결하면 끝입니다.
mutations에 있는 메서드를 불러오기 위해선 commit을 사용합니다.
store.js
import { createStore } from "vuex";
const store = createStore({
state() {
return {
like: 0,
};
},
mutations: { // 여기
plusLike(state, payload) {
state.like += payload;
},
},
});
export default store;
App.vue
<template>
<div>{{ $store.state.likes }}</div> <!--여기-->
<button @click="$store.commit('plusLike', 10)">좋아요</button>
</template>
...
또 다음과 같이 두 번째 인자 payload로 데이터를 실어서 보낼 수 도 있습니다.
actions
store.js
import { createStore } from "vuex";
import axios from "axios";
const store = createStore({
state() {
return {
post: {},
};
},
// 2
mutations: {
setPost(state, data) {
state.post = data;
},
},
// 1
actions: { // 3
getData(context) {
axios.get(URL).then((result) => {
context.commit('setPost', result.data) // 2
});
},
},
});
export default store;
- ajax 요청이나 시간이 다소 걸리는 작업을 집어넣는 공간입니다. 비동기 함수의 순차적 실행을 보장받는 공간이라 할 수 있습니다.
- state를 건드리는 메서드는 mutations에 명시해야 하기 때문에 통신을 통해 받은 데이터를 저장할 함수를 만들고 연결합니다.
- 통상적으로 인자를 context로 적지만 이는 store를 지칭하는 것입니다.
App.vue
<template>
<h4>{{ $store.state.more }}</h4>
<button @click="$store.dispatch('getData')">더보기</button> // 여기
</template>
...
actions에 있는 메서드를 실행하기 위해선 dispatch를 사용해야 합니다.
mutations에 있는 메서드를 실행하기 위해서는 commit을 사용했었습니다.
mapState
<template>
<p>{{ now1() }} {{ count }}</p>
<p>{{ now2 }} {{ count }}</p> <!--값을 가지고 있기에 데이터 바인딩할 때 함수를 실행하지 않고 지칭함-->
<button @click="count++"></button>
</template>
<script>
export default {
name: "App",
data() {
return {
count: 0,
};
},
computed: {
now2() {
return new Date();
},
},
methods: {
now1() {
return new Date();
},
},
}
</script>
computed와 methods 둘 다 함수를 저장하는 공간입니다.
methods 함수는 당연히도 사용할 때마다 실행됩니다.
하지만 computed 함수는 아래 화면처럼 처음에만 실행되고 실행되지 않았습니다.
마치 useEffect에 종속성 배열을 두지 않은 상태랑 같죠?
아무튼 보통 이 computed를 이용하여 아래처럼 state를 불러오는 게 일반적입니다.
그리고 반드시 반환 값이 있어야 합니다.
App.vue
<template>
<!--div>{{ $store.state.likes }}</div-->
<div>{{ likes }}</div>
</template>
<script>
export default {
name: 'App',
computed: {
likes() {
return this.$store.state.likes;
},
},
}
</script>
하지만 저렇게 전역으로 관리하는 모든 데이터를 다 선언하기엔 좀 번거롭다 생각할 수 있습니다.
그걸 mapState가 도와줄 수 있습니다. 활용을 잘 보여줄 수 있게 state를 늘려봅니다.
store.js
import { createStore } from "vuex";
const store = createStore({
state() {
return {
name: 'KIM',
height: 190,
strong: true,
};
},
});
export default store;
App.vue
<template>
<div>{{ name }}</div>
<div>{{ height }}</div>
<div>{{ strong }}</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'App',
computed: {
...mapState(['name','height', 'strong']), // 1
...mapState({myName: 'name', }) // 2
},
methods: {
..mapMutations(['plusLike']), // 3
}
}
</script>
- mapState에 state의 이름들을 문자열로 배열에 담아 쉽게 그 state를 이용할 수 있습니다.
- 객체로 담아 별칭을 부여할 수 도 있습니다.
- mutations 메서드도 쉽게 가져다 쓸 수 있습니다.
actions도 가져다 쓸 수 있으며 더 다양한 디테일들은 공식문서에서 확인할 수 있습니다.
What is Vuex? | Vuex
What is Vuex? Pinia is now the new default The official state management library for Vue has changed to Pinia. Pinia has almost the exact same or enhanced API as Vuex 5, described in Vuex 5 RFC. You could simply consider Pinia as Vuex 5 with a different na
vuex.vuejs.org