vue에서 상태 관리를 하기 위한 라이브러리인 vuex에 대해 정리하고자 한다.
new Vue({
// state
data() {
return {
counter: 0
};
},
// view
template: `
<div>{{ counter }}</div>
<button @click="increment">PLUS</button>
`,
// actions
methods: {
increment() {
this.counter++;
}
}
});
v-model을 사용하여 양방향 바인딩 가능 (two-way binding)
Event Bus 문제점
컴포넌트가 많아지면 어디서 어디로 보냈는지 관리가 되지 않음
(컴포넌트 간 데이터 흐름을 파악하기 어려움)
Vue 버전에 맞는 Vuex 버전 확인
- Vue 2 & Vuex 3
- Vue 3 & Vuex 4
- Vuex 저장소가 일반 전역 개체와 다른 두 가지
- Vuex store는 반응형
Vue 컴포넌트는 상태를 검색할 때 저장소의 상태가 변경되면 효율적으로 대응하고 업데이트 함- 상태를 직접 변경할 수 없음
저장소의 상태를 변경하는 유일한 방법은 명시적인 commit을 이용한 변이
이렇게하면 모든 상태에 대한 추적이 가능한 기록이 남을 수 있으며 툴을 사용하여 앱을 더 잘 이해할 수 있음
store.commit({
type: 'increment',
amount: 10
})
/*-----------------------------*/
// store.js
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.dispatch('incrementAsync', {
amount: 10
})
// or
store.dispatch({
type: 'incrementAsync',
amount: 10
})
/*-----------------------------*/
// store.js
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
npm install vuex
Vue.use()
통해 Vuex 명시적으로 추가import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
<script> 태그 사용 시, 해당 작업 필요 없음
npm install es6-promise
import 'es6-promise/auto'
를 아무 곳에나 추가하기├── store
│ └── index.js
├── App.vue
└── main.js
<template>
<div id="app">
<div>
<label>{{ storeGetMsg }}</label>
<input type="number" v-model.number="num"/>
<button @click="onChangedMsg">PLUS</button>
</div>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
num: 1
}
},
computed: {
storeGetMsg () {
return this.$store.getters.getMsg
}
},
methods: {
onChangedMsg () {
this.$store.dispatch('callMutation', { num: this.num })
}
}
}
</script>
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
num: 0
},
mutations: {
changeNumber (state, val) {
state.num += val
}
},
actions: {
callMutation ({ state, commit }, { num }) {
commit('changeNumber', num)
}
},
getters: {
getMsg (state) {
return `calculated => ${state.num}`
}
}
})
import Vue from 'vue'
import App from './App.vue'
import store from './store/index'
Vue.config.productionTip = false
new Vue({
store: store,
render: h => h(App),
}).$mount('#app')
<template>
<div>
<!-- <p>{{ $store.state.num }}</p> -->
<p>{{ num }}</p>
<button @click="clickBtn">popup message</button>
<button @click="getPopup">show popup</button>
</div>
</template>
<script>
// App.vue
import { mapState, mapGetters, mapMutations, mapAcations } from 'vuex'
export default {
// this.$store.state.num == mapState(['num'])
// this.$store.getters.countedNum == mapGetters(['countedNum'])
computed() {
...mapState(['num']),
...mapGetters(['countedNum'])
},
methods: {
...mapMutations(['clickBtn', 'addNumber']),
...mapActions({
getPopup: 'showPopup' // 컴포넌트에서 쓸 명칭 : store의 action 명
})
}
}
</script>
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
import coffee from "./modules/groups";
export default new Vuex.Store({
state: () => {},
getters: {},
actions: {},
mutations: {},
modules: {
groups: groups,
guest: {
actions: {
login () { ... } // -> dispatch('login')
}
},
account: {
namespaced: true,
// module assets
state: () => ({ ... }), // module state is already nested and not affected by namespace option
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// nested modules
modules: {
// inherits the namespace from parent module
myPage: {
state: () => ({ ... }),
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// further nest the namespace
posts: {
namespaced: true,
state: () => ({ ... }),
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
참고
https://vuex.vuejs.org
https://vuejs.org/v2/guide/components.html
https://vuejs.org/v2/api/#v-model
https://bbosong-develop.tistory.com/3
https://kdydesign.github.io/2019/05/09/vuex-tutorial
https://dev-jsk.tistory.com/77