본문 바로가기
개발 관련 지식/Vue.JS

Vuex : vue 상태 관리 패턴

by 권태일1147 2020. 3. 15.

컴포넌트들에 공유된 상태를 추출하고 이를 전역 싱글톤으로 관리해야 한다. 모든 컴포넌트는 트리에 상관없이 상태에 액세스하거나 동작을 트리거 할 수 있다.

싱글톤 : 특정 클래스에서 인스턴스가 만들어지고, 어디서든지 그 하나의 인스턴스에 접근할 수 있도록 하기 위한 패턴

또한 상태 관리 및 특정 규칙 적용과 관련된 개념을 정의하고 분리함으로써 코드의 구조와 유지 관리 기능을 향상시킨다. 

 

Vuex를 다루는 디렉토리 store

store는 반응형이다. Vue 컴포넌트는 상태(state)를 검색할 때 store의 상태가 변경되면 효율적으로 대응하고 업데이트 한다.

저장소의 상태를 직접 변경할 수 없다. 저장소의 상태(state)를 변경하는 유일한 방법은 명시적인 커밋을 이용한 변이(mutations)이다. 이렇게 하면 모든 상태에 대한 추적이 가능한 기록이 남을 수 있다.

 

Vuex는 store 옵션(Vue.use(Vuex)에 의해 가능)으로 루트 컴포넌트의 모든 자식 컴포넌트에 저장소(store)를 '주입'하는 메커니즘을 제공한다.

Vue.use(Vuex)를 사용함으로써 store는 루트의 모든 하위 컴포넌트에 주입되고 this.$store로 store를 사용할 수 있다.

main.js에 vuex를 나타내는 store를 Vue가 사용하도록 하면 모든 하위 컴포넌트에서 $store로 state, getters, actions, mutations을 사용할 수 있다.

 

컴포넌트에서 dispatch로 actions를 실행하고, actions에서 commit해서 mutations으로 state를 변경하고, state를 getters로 컴포넌트에 불러오는 동작이 Vuex.

컴포넌트에서 actions을 호출해서 사용하면 store-index.js의 actions안에서 store.commit() 함수로 mutations을 호출해서 실행하여 state를 바꾸고 컴포넌트에서 getters로 state를 불러오는 구조로 사용됨.

actions로 api를 작동시키고 api로 crud된 데이터를 actions안에서 mutations로 state에 넣는다. getters로 컴포넌트에서 간편하게 호출한다.

 

 

getters : 완성된 state를 가져오기 위한 핸들러.

컴포넌트에서 데이터에 접근할 때 중복된 코드를 반복하게 되는 경우가 많은데 그걸 줄이기 위해 사용

Getters는 첫번째 인자로 state를 받는다.

const store = new Vuex.Store({
    state: {
        todos: [
            { id: 1, text: '...', done: true },
            { id: 2, text: '...', done: false },
        ]
    },
    getters: {
        doneTodos: state => {
            return state.todos.filter(todo => todo.done)
        }
    }
})

getters는 store.getters 객체에 노출되고, 속성으로(store.getters.doneTodos) 값에 접근할 수 있다.

 

mapGetters 헬퍼 (mapGetters는 vuex에 내장 되어 있는 객체)

mapGetters 헬퍼는 저장소 getters를 로컬 계산된 속성에 매핑한다.

// vue component

import { mapGetters } from 'vuex'
 
export default {
    // ...
    computed: {
        ...mapGetters([
            'doneTodosCount',
            'anotherGetter',
            // ...
        ])
 
        //다른 이름으로 매핑하려면 객체를 사용
        ...mapGetters({
            doneCount: 'doneTodosCount'
        })
    }
}

 

 

mutations : state를 변이 시키는 핸들러.

mutations : state를 변경하는 유일한 방법 - 여러개의 컴포넌트가 하나의 데이터에 접근할 때 효율적으로 관리하기 위함.(비동기적임)

mutation을 직접 호출 할 수는 없다. 예를 들어

state : {
    count: 1
},
mutations: {
    increment (state) {  // mutations의 핸들러 함수, 첫번째 매개변수는 state을 받아야 한다. 
        state.count++
    }
}

이런식으로 있으면 increment()를 바로 불러서 실행할 수 없고, actions에서 store.commit('increment')을 호출해서 mutations를 실행한다.

 

mutations에 대해서 store.commit()에 추가 매개변수를 사용할 수 있는 것을 페이로드(payload)라고 한다.

state : {
    count: 1
},
mutations: {
    increment (state, payload) {  //mutations의 핸들러 함수, 첫번째 매개변수는 state을 받아야 한다.
        state.count += payload.amount
    }
}
actions:{
	testExeIncre(store){
        store.commit('increment', {amount:10})
    }
}

Vue의 반응성 규칙을 따르는 변이

Vuex 저장소의 state는 Vue에 의해 반응하므로, state를 변경하면 state를 관찰하는 Vue 컴포넌트가 자동으로 업데이트 됩니다. 이것은 또한 mutations가 일반 Vue로 작업할 때 동일한 반응을 보일 수 있다.

 

 

actions : mutation을 적용 하는 핸들러.

actions는 mutations와 유사하다. 다른점은 mutations은 state를 변경하고,

actions는 state를 변경하는 대신에 mutation에 대한 커밋을 한다.

컴포넌트에 actions를 실행시키는 것은 dispatch

actions 핸들러는 store 인스턴스의 같은 메소드들/프로퍼티 세트를 드러내는 컨택스트 객체를 받는다. 그래서 context.commit을 호출하여 mutation을 commit하거나 context.state와 context.getters를 통해 state와 getters에 접근 할 수 있다. 근데 이 context는 store 인스턴스 그 자체가 아니다. 위에서 사용한 store.commit()이 사실 context.commit()이다.

const store = new Vuex.Store({
    state: {
        count: 0
    },
    mutations: {
        increment (state) {
            state.count++
        }
    },
    actions: {
        increment (store) {  // context를 store라는 이름으로 사용했다.
            store.commit('increment')
        }
    }
})

 

actions는 store.dispatch 함수로 호출한다.

store.dispatch('increment')

 

mutations는 동기적이어야 하지만, actions는 비동기 작업을 수행할 수 있다.

actions: {
    incrementAsync ({ commit }) {
        setTimeout(() => {
            commit('increment')
        }, 1000)
    }
}

 

actions도 payload 타입을 지원한다.

store.dispatch('incrementAsync', { amount: 10 })

 

 

 

mapGetters, mapActions, mapMutations를 사용하기 위해 mappers.js를 파일을 따로 만들었다.

프로젝트에서는 mappers.js에 mapGetters, mapActions, mapMutations를 import해서 Getter, Action, Mutation이라는 함수를 만들고

mapGetters를 사용하려는 vue 컴포넌트에서 mappers.js를 import 해서 해당하는 속성에 존재하는 data를 Getter에 추가하고 Getter에 추가된 data는 computed 속성으로 추가되어 간편하게 호출해서 사용한다.

// mappers.js

import { mapGetters, mapActions, mapMutations } from 'vuex'
import { createDecorator } from 'vue-class-component'

export function Getter (getterName) {
  return createDecorator(options => {
    if (!options.computed) {
      options.computed = {}
    }
    Object.assign(options.computed, mapGetters([getterName]))
  })
}

export function Action (actionName) {
  return createDecorator(options => {
    if (!options.methods) {
      options.methods = {}
    }
    Object.assign(options.methods, mapActions([actionName]))
  })
}

export function Mutation (mutationName) {
  return createDecorator(options => {
    if (!options.methods) {
      options.methods = {}
    }
    Object.assign(options.methods, mapMutations([mutationName]))
  })
}
// store index.js

import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist'

export default new Vuex.Store({
  state: {
      example : []
  },
  getters: {
      example : state => {
      	state.example
      }
  },
  mutations: {
      examMutation(state, payload){
          state.example = payload
      }
  },
  actions: {
      examCommit(store){
          const someArr = [1, 2, 3]
          store.commit('examMutation', someArr)
      }
  },
  plugins: [new VuexPersistence().plugin]
})
// vue component

import { Vue, Component, Prop} from 'vue-property-decorator'
import { Getter, Action } from '@/mappers'

@Component({
  components: {
    // ...
  }
})

@Action('examCommit')

@Getter('example')

export default class ExamComponent extends Vue {
    // this.example 로 불러오고
    // this.examCommit 을 호출한다.
}

'개발 관련 지식 > Vue.JS' 카테고리의 다른 글

vue create 프로젝트 read-only  (0) 2020.05.29
Vue - computed, watch  (0) 2020.03.17
vee-validate  (0) 2020.03.10
virtual scroll  (0) 2020.03.05
vue.js project 만들기  (0) 2020.03.05