아무런 관계 없는 두 컴포넌트간에 데이터를 주고받으려면?
⇒ store로 데이터와 메소드를 관리한다
1. JS로 상태관리 구현
- 동일한 객체를 data메소드의 리턴 값으로 쓰면, 해당 객체를 쓰는 컴포넌트의 data는 동기화된다(반응형)
- 즉, 한 컴포넌트에서 객체 데이터를 변경하면, 다른 컴포넌트의 객체 데이터도 같이 변경된다
⇒ data는 proxy로 관리되기 때문!
export const store = { msg: '', count: 0 }
<template> <h1 @click="increaseCount">{{ count }}</h1> </template> import { store } from '~/store' .. data() { return store //msg, count 데이터를 가짐 }, methods: { increaseCount() { this.count += 1 //두 컴포넌트중 하나라도 이 메소드로 데이터를 변경하면 다른 한 컴포넌트의 데이터도 같이 변경 } }
- data 메소드 외부에서도 반응형이 되려면, vue의 reactive 메소드 안에 객체를 넣어서 값으로 주입해주면 된다
import {reactive} from 'vue' export const store = reactive({ msg: '', count: 0 }) export const mutations = { increaseCount() { store.count += 1 //store가 reactive 없이 그냥 객체였다면, //이 메소드로는 컴포넌트들끼리 동기화 불가 } }
- 이렇게 state, getters, mutations, actions로 기능을 분리해서 상태관리를 직접 만들 수 있다
- state : 상태 데이터들
- 컴포넌트의 data()에서 사용
- getters : 상태 데이터를 이용해서 만든 새로운 데이터들
- 컴포넌트의 computed에서 사용
- mutations : 상태 데이터를 조작하는 메소드들
- actions : 상태와 관련 없는 메소드들, 비동기(ex. api 통신)
2. Vuex로 상태관리
[설치]
npm install vuex
[store 생성]
- vuex의
createStore
메소드에 state, getters, mutations, actions를 모은 객체 주입 - mutations, getters의 메소드들은 매개변수로 state를 받을 수 있다. 이를 이용해 state에 접근가능
- actions의 메소드에서는 매개변수로 context 객체를 받을 수 있다
- context 안에는 state, getters, commit(뮤테이션), dispatch(액션)가 있다
- 당연히 매개변수들의 이름은 아무거나 ㄱㅊ
import { createStore } from "vuex"; export default createStore({ state() { return { msg: 'hello', count: 0 } }, getters: { reverseMsg(state) { return state.msg.split('').reverse().join('') } }, mutations: { increaseCount(state) { state.count+=1 }, insertTodoText(state, newVal) { state.msg = newVal } }, actions: { async fetchTodo(context, payload) { //payload는 그냥 데이터 매개변수.. const todo = await fetch('https://jsonplaceholder.typicode.com/todos/1') .then(res => res.json()) context.commit('insertTodoText', todo.title) } } })
- 2를 app 플러그인으로 넣어줌(
app.use(store)
)
.. import store from './store' const app = createApp(App) app.use(store) app.mount('#app')
[store 사용]
- 전역에서 state 접근 :
this.$store.state.상태
- 전역에서 getters 접근 :
this.$store.getters.게터
- 전역에서 뮤테이션 메소드 실행 :
this.$store.commit(’뮤테이션이름’)
- 전역에서 액션 메소드 실행 :
this.$store.dispatch(’액션이름’)
- state, getters 데이터 모두 data메소드가 아닌 computed에 등록해야함에 주의!!
- 뮤테이션과 액션은 methods에서 호출
ex) App에서 버튼으로 액션과 뮤테이션을 실행하여 state를 변경하면, 같은 store를 쓰는 Hello의 데이터까지 반영된다
<template> <h1>{{ msg }}</h1> <button @click="$store.dispatch('fetchTodo')"> changeMsg </button> <h2>{{ count }}</h2> <button @click="$store.commit('increaseCount')"> +1 </button> <Hello /> </template> <script> import Hello from '~/components/Hello/Hello' export default { components: { Hello }, computed: { msg() { //state 데이터 return this.$store.state.msg }, count() { //state 데이터 return this.$store.state.count } }, } </script>
<template> <h1>{{ reverseMsg }} / {{ count }}</h1> </template> <script> export default { computed: { count() { //state 데이터 return this.$store.state.count }, reverseMsg() { //getters 데이터 return this.$store.getters.reverseMsg } } } </script> <style scoped lang="scss"> h1 { color: pink } </style>
store를 모듈로 관리하기
[생성]
- 분리하고 싶은만큼 js파일을 만들어서, 일반 js 객체를 export
- 이전에 createStore 안에 주입했던 객체와 똑같다
namespaced: true
옵션을 추가한다
export default { namespaced: true, state() { .. }, ..
- 최상위 store로 index.js에서 createStore를 해서,
modules
옵션을 추가한 후 앞서 만든 js객체들을 등록
import 스토어모듈1 from '..' import 스토어모듈2 from '..' export default createStore({ .. modules: { 스토어모듈1, 스토어모듈2 } })
[사용]
방법1) ⇒ 기존처럼 객체 값으로
- state는
this.$store.state.모듈이름.state이름
- getters는
this.$store.getters[’모듈이름/게터이름’]
- mutations은
this.$store.commit(’모듈이름/뮤테이션이름’)
으로 실행
방법2) ⇒ vuex의 map 사용
import { mapState, mapGetters, mapMutations, mapActions } from ‘vuex’ export default { computed: { ...mapState('모듈이름', [ '상태이름1', '상태이름2' ,, ]), ...mapGetters('모듈이름', [ '게터이름1', '게터이름2' ,, ]) }, methods: { ...mapMutations('모듈이름', [ '뮤테이션이름1', '뮤테이션이름2' ,, ]), ...mapActions('모듈이름', [ '액션이름1', '액션이름2' ,, ]) } }
- 최상위 store에 등록된 것은
…mapState([’상태이름’])
처럼 모듈 이름 없이 배열에 이름으로 바로 등록할 수 있다
export default { computed: { …mapState([’상태이름’]), .. }