모듈(Module)

단일 상태 트리를 사용하기 때문에 애플리케이션의 모든 상태가 하나의 큰 객체 안에 포함되게 된다.
그러나 규모가 커짐에 따라 저장소가 매우 비대해질 수 있다.

이를 위해 Vuex는 저장소를 모듈로 나눌 수 있다.
각 모듈은 자체 상태(state), 변이(mutation), 액션(action), 게터(getter) 및 심지어 중첩된 모듈을 포함 할 수 있다.

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA'의 상태
store.state.b // -> moduleB'의 상태

지역 상태 모듈

모듈의 mutations와 getter 내부에서 첫 번째 전달인자는 모듈의 지역 상태(state)가 된다.

const moduleA = {
  state: () => ({
    count: 0
  }),
  mutations: {
    increment (state) {
      // state는 모듈의 지역 state를 의미
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

모듈의 actions에서 context.state는 지역 상태이고 context.rootState는 루트 상태이다.

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

모듈의 getters에서 세번째 인수로 루트 상태를 받는다.

const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}

네임스페이스

기본적으로 모듈 내의 action, mutation, getter는 여전히 전역 네임 스페이스 아래에 등록된다.
여러 모듈이 동일한 action∙mutation 유형에 반응 할 수 있다.

모듈이 독립적이거나 재사용되기를 원한다면 네임스페이스에 namespaced: true를 설정하면 된다.
해당 모듈의 모든 getter, action∙mutation을 자동으로 등록된 모듈의 경로를 기반하여 네임스페이스 지정된다.

const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 모듈 자산
      state: () => ({ ... }), // 모듈 상태는 이미 중첩되어 있고, 네임스페이스 옵션의 영향을 받지 않음
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 중첩 모듈
      modules: {
        // 부모 모듈로부터 네임스페이스를 상속받음
        myPage: {
          state: () => ({ ... }),
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 네임스페이스를 더 중첩
        posts: {
          namespaced: true,

          state: () => ({ ... }),
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})

네임 스페이스의 getter와 action은 지역화된 getters, dispatch, commit을 받는다.
즉, 동일한 모듈 안에서 접두어 없이 모듈을 사용할 수 있다.
네임 스페이스 옵션 값을 바꾸어도 모듈 내부의 코드에는 영향을 미치지 않는다.

+ 네임스페이스 모듈 내부에서 전역 자산 접근

전역 상태나 getter를 사용하고자 한다면,
rootState와 rootGetters가 getter 함수의 3번째와 4번째 인자로 전달되며,
또한 action 함수에 전달된 'context' 객체의 속성으로도 노출된다.

전역 네임스페이스의 action을 dispatch하거나, mutation을 commit하려면
dispatch와 commit에 3번째 인자로 { root: true }를 전달하면 된다.

modules: {
  foo: {
    namespaced: true,

    getters: {
      // `getters`는 해당 모듈의 지역화된 getters
      // getters의 4번째 인자를 통해서 rootGetters 사용 가능
      someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -> 'foo/someOtherGetter'
        rootGetters.someOtherGetter // -> 'someOtherGetter'
      },
      someOtherGetter: state => { ... }
    },

    actions: {
      // 디스패치와 커밋도 해당 모듈의 지역화된 것
      // 전역 디스패치/커밋을 위한 `root` 옵션 설정 가능
      someAction ({ dispatch, commit, getters, rootGetters }) {
        getters.someGetter // -> 'foo/someGetter'
        rootGetters.someGetter // -> 'someGetter'

        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'

        commit('someMutation') // -> 'foo/someMutation'
        commit('someMutation', null, { root: true }) // -> 'someMutation'
      },
      someOtherAction (ctx, payload) { ... }
    }
  }
}

+ 네임스페이스 모듈에서 전역 액션 등록

네임스페이스 모듈에서 전역 액션을 등록하려면, root: true를 표시하고 handler 함수에 액션을 정의하면 된다.

{
  actions: {
    someOtherAction ({dispatch}) {
      dispatch('someAction')
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -> 'someAction'
        }
      }
    }
  }
}

+ 헬퍼에서 네임스페이스 바인딩

mapState, mapGetters, mapActions, mapMutations 헬퍼에서 네임스페이스 모듈을
컴포넌트에 바인딩할 순 있지만 이를 하게 되면 조금 장황하게 된다.

computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])
}

이러한 경우, 모듈의 네임스페이스 문자열을 헬퍼의 첫 번째 인수로 전달하여 해당 모듈을 컨텍스트로 사용하여 모든 바인딩을 할 수 있다.
위의 예시는 아래와 같이 단순화 할 수 있는 것이다.

computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}

또한 createNamespacedHelpers를 사용하여 네임스페이스 헬퍼를 생성할 수도 있다.
전달된 네임스페이스 값으로 바인딩된 새로운 컴포넌트 바인딩 헬퍼를 가진 객체를 반환한다.

import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    // `some/nested/module`에서 찾음
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // `some/nested/module`에서 찾음
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}

동적 모듈 등록

store.registerModule 메소드로 저장소가 생성 된 후에 모듈을 등록 할 수 있다.

import { createStore } from 'vuex'

const store = createStore({ /* options */ })

store.registerModule('myModule', {
  // ...
})
// `nested/myModule` 중첩 모듈 등록
store.registerModule(['nested', 'myModule'], {
  // ...
})

모듈의 상태는 store.state.myModulestore.state.nested.myModule로 노출된다.

동적 모듈 등록을 하면 다른 Vue 플러그인도 애플리케이션의 Store에 모듈을 연결하여 상태 관리에 Vuex를 활용할 수 있다.

store.unregisterModule(moduleName)을 사용하여 동적으로 등록 된 모듈을 제거할 수도 있다.
단, 이 방법으로 정적 모듈(저장소 생성시 선언 됨)은 제거 할 수 없다.

SSR(Server Side Rendered) 앱에서 상태를 유지하는 것처럼
새 모듈을 등록할 때 preserveState 옵션을 사용하면 이전 상태를 유지하고자 할 수 있다.
(ex. store.registerModule('a', module, { preserveState: true }))

profile
까먹지마도토도토잠보🐘

0개의 댓글