Vuex
Vuex는 vuejs에서 컴포넌트들의 상태(state)를 관리하는 라이브러리이다.
(React의 Flux 패턴에서 차용한 것이라고 한다.)
Vuex 설치하기
Vuex 세팅하기
Vuex는 상태(state)를 저장하고 있는 저장소(store)를 가지고 있다.
Vuex가 설치되었다면 이 저장소를 관리하는 파일을 생성해주자.
1
2
3
4
5
6
7
8
9
10
11
| import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {}
});
|
main.js에선 만들어준 src/store/store.js를 import 해온 뒤 Vue 인스턴스에 주입한다.
1
2
3
4
5
6
7
8
9
10
| import Vue from 'vue';
import App from './App.vue';
import { store } from './store/store';
Vue.config.productionTip = false;
new Vue({
store,
render: (h) => h(App),
}).$mount('#app');
|
Vuex store 속성
Vuex store에는 state와 같은 속성이 크게 5가지가 있다.
state, getters, mutations, actions, modules
state
state는 상태(state)의 집합이다.
Vuex는 단일 상태 트리(single state tree)를 사용하기 때문에 이 집합 내에서 현재 상태를 쉽게 찾을 수 있다.
1
2
3
4
5
6
7
8
9
10
| import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
count: 0,
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| <template>
<div>{{ count }}</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count
}
}
}
</script>
|
mapState
1
2
3
4
5
6
7
8
9
10
11
12
13
| <template>
<div>{{ count }}</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['count']),
}
}
</script>
|
getters
state 값에 접근하는 속성이자 computed() 처럼 미리 연산된 값에 접근하는 속성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
count: 0,
},
getters: {
doubleCount(state) {
return state.count * 2
}
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| <template>
<div>{{ doubleCount }}</div>
</template>
<script>
export default {
computed: {
doubleCount() {
return this.$store.getters.doubleCount
}
}
}
</script>
|
mapGetters
1
2
3
4
5
6
7
8
9
10
11
12
13
| <template>
<div>{{ doubleCount }}</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters({ doubleCount: 'doubleCount' }),
}
}
</script>
|
mutations
state의 값을 변경할 수 있는 유일한 방법이자 메서드
뮤테이션은 commit()으로 동작시킨다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
addCount(state, payload) {
state.count = state.count + payload.num;
}
}
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| <template>
<div><button @click="addCount">{{ count }}</button></div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count
}
},
methods: {
addCount() {
this.$store.commit('addCount', { num: 20 });
}
}
}
</script>
|
mapMutations
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <template>
<div><button @click="addCount({ num: 20 })">{{ count }}</button></div>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
computed: {
...mapGetters({ count: 'count'}),
},
methods: {
...mapMutations(['addCount']),
},
}
</script>
|
actions
비동기 처리 로직을 선언하는 메서드, 비동기 로직을 담당하는 mutations
데이터 요청, Promise, ES6 async 와 같은 비동기 처리는 모두 actions에 선언한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
product: [],
},
mutations: {
setData(state, payload) {
state.product = fetchedData;
}
},
actions: {
fetchProductData(context) {
return axios.get('https://domain.com/product/1')
.then(res => context.commit('setData', res));
}
}
});
|
1
2
3
4
5
| methods: {
getProduct() {
this.$store.dispatch('fetchProductData');
}
}
|
Vuex 헬퍼 함수가 주는 간편함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
price: 100,
},
getters: {
originalPrice(state) {
return state.price;
},
doublePrice(state) {
return state.price * 2;
},
triplePrice(state) {
return state.price * 3;
},
},
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| <template>
<div class="root">
<p>{{ originalPrice }}</p>
<p>{{ doublePrice }}</p>
<p>{{ triplePrice }}</p>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['originalPrice', 'doublePrice', 'triplePrice']),
}
}
</script>
|
Vuex 모듈화
1
2
3
4
5
6
7
8
9
10
11
| import Vue from 'vue';
import Vuex from 'vuex';
import todoApp from './todoApp/todoApp.js';
Vue.use(Vuex);
export const store = new Vuex.Store({
modules: {
todoApp,
},
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| import * as getters from './getters.js'; // import 방법1
import mutations from './mutations.js'; // import 방법2
import actions from './actions.js';
const storage = {
fetch() {
let arr = [];
if (localStorage.getItem('todoItems') !== null) {
arr = JSON.parse(localStorage.getItem('todoItems'));
}
return arr;
},
};
const state = {
todoItems: storage.fetch(),
};
export default {
state,
getters,
mutations,
actions,
};
|
1
2
3
4
5
6
| // export 방법1
const getTodoItems = (state) => {
return state.todoItems;
};
export { getTodoItems };
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // export 방법2
export default {
addTodo(state, payload) {
const time = new Date().toJSON().replaceAll(/[^0-9]/g, '');
const obj = { time: time, completed: false, item: payload.newTodoItem };
state.todoItems.push(obj);
this.commit('setLocalStorage');
},
removeTodo(state, payload) {
const idx = state.todoItems.indexOf(payload.todoItem);
state.todoItems.splice(idx, 1);
this.commit('setLocalStorage');
},
toggleComplete(state, payload) {
const idx = state.todoItems.indexOf(payload.todoItem);
state.todoItems[idx].completed = !state.todoItems[idx].completed;
this.commit('setLocalStorage');
},
clearTodo(state) {
state.todoItems = [];
this.commit('setLocalStorage');
},
setLocalStorage(state) {
localStorage.setItem('todoItems', JSON.stringify(state.todoItems));
},
};
|
참고