본문 바로가기

Java Scripts/Vue.js

Vuex - Modules

728x90

Modules

As our application gets bigger, we’re going to end up with a gigantic store.js file. This is where Vuex modules come in, allowing us to keep our Vuex code organized and easier to test.

애플리케이션이 커질수록 거대한 store.js 파일이 생성될 것입니다. 여기가 Vuex 모듈이 필요한 부분으로, Vuex 코드를 체계적으로 유지하고 테스트하기 쉽게 해줍니다.

 

Problem: We need to organize our code

Solution

Vuex has an option called modules which makes it simple to split out different parts of your state into different files. 

Vuex에는 모듈이라는 옵션이 있어 상태의 여러 부분을 여러 파일로 간단하게 분할할 수 있습니다.

For example, if your app has events and users it doesn’t make sense to pile all the state, mutations, actions, and getters into one big /src/store.js file.

예를 들어 앱에 이벤트와 사용자가 있는 경우 모든 state , mutations , actions 및 getters 를 하나의 큰 /src/store.js 파일에 쌓는 것은 의미가 없습니다.

 

 

Back to our Example app

Creating a store directory, and move our current store.js file inside of it.

store  디렉토리를 생성하고 현재 store.js 파일을 그 안으로 옮깁니다.

Modifying our main.js to look inside our new directory.

새 디렉토리 내부를 확인하기 위해 main.js를 수정합니다.

import store from './store'

to

 import store from './store/store'

Building Our First Module

As in the future as we build out authentication in our example app we’ll be placing a lot more code in here. Hence create a new modules folder with a new user.js file which just contains our user state.

앞으로도 예제 앱에서 인증을 구축하면서 여기에 더 많은 코드를 배치할 것입니다. 따라서 사용자 state만 포함하는 새 user.js 파일을 사용하여 새 모듈 폴더를 만듭니다.

 

/src/store/modules/user.js

    export const state = {
      user: {
        id: 'abc123',
        name: 'Adam'  // I removed the last name Jahr here our title is on one line
      }
    }

 

/src/store/store.js

In order to use this module, we need to include it inside our store.js.

이 모듈을 사용하려면 store.js 안에 모듈을 포함해야 합니다.

    ...
    import * as user from '@/store/modules/user.js'
    // This pulls in all the constants in user.js 
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      modules: {
        user  // Include this module
      },
      state: {
        categories: [
          'sustainability',
          // ...

 

/src/views/EventList.vue

need to add another .user for module name

모듈 이름에 다른 .user를 추가해야 합니다.

    <template>
      <div>
        <h1>Events for {{ user.user.name }}</h1>
        ...
    </template>

 

/src/views/EventCreate.vue

When referencing the state we need to set user to user.user.

상태를 참조할 때 user를 user.user로 설정해야 합니다.

    <script>
    ...
        createFreshEventObject() {
          const user = this.$store.state.user.user // <----
          const id = Math.floor(Math.random() * 10000000)
          ...

Creating an Event Module

 /src/store/modules/event.js

move all of our event State, Mutations, Actions, and Getters into its own event.js module.

모든 이벤트 상태, Mutations , Actions 및 Getter를 자체 event.js 모듈로 이동합니다.

    import EventService from '@/services/EventService.js'
    
    export const state = {
      events: [],
      eventsTotal: 0,
      event: {}
    }
    export const mutations = {
      ADD_EVENT(state, event) {
        state.events.push(event)
      },
      SET_EVENTS(state, events) {
        state.events = events
      },
      SET_EVENTS_TOTAL(state, eventsTotal) {
        state.eventsTotal = eventsTotal
      },
      SET_EVENT(state, event) {
        state.event = event
      }
    }
    export const actions = {
      createEvent({ commit }, event) {
        return EventService.postEvent(event).then(() => {
          commit('ADD_EVENT', event)
        })
      },
      fetchEvents({ commit }, { perPage, page }) {
        EventService.getEvents(perPage, page)
          .then(response => {
            commit('SET_EVENTS_TOTAL', parseInt(response.headers['x-total-count']))
            commit('SET_EVENTS', response.data)
          })
          .catch(error => {
            console.log('There was an error:', error.response)
          })
      },
      fetchEvent({ commit, getters }, id) {
        var event = getters.getEventById(id)
        if (event) {
          commit('SET_EVENT', event)
        } else {
          EventService.getEvent(id)
            .then(response => {
              commit('SET_EVENT', response.data)
            })
            .catch(error => {
              console.log('There was an error:', error.response)
            })
        }
      }
    }
    export const getters = {
      getEventById: state => id => {
        return state.events.find(event => event.id === id)
      }
    }

/src/store/store.js

need to use this module inside our store.js:

store.js 내에서 이 모듈을 사용해야 합니다.

    import Vue from 'vue'
    import Vuex from 'vuex'
    import * as user from '@/store/modules/user.js'
    import * as event from '@/store/modules/event.js'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      modules: {
        user,
        event
      },
      state: {
        categories: [ ... ]
      }
    })

/src/views/EventList.vue

Our events, eventTotal, and event State must all be accessed by event.events, event.eventTotal, and event.event.

이벤트, eventTotal 및 event State는 모두 event.events, event.eventTotal 및 event.event를 통해 액세스되어야 합니다.

We changed our mapState to just access event (this is mapping to our module name which is event). Then we just had to make sure to use event. when we accessed parts of the state.

우리는 이벤트에만 액세스하도록 mapState를 변경했습니다(이것은 이벤트인 모듈 이름에 매핑됩니다). 우리가 state 의 일부에 접근했을 때  그런 다음 이벤트를 사용해야 했습니다.

    <template>
      <div>
        <h1>Events for {{ user.name }}</h1>
        <EventCard v-for="event in event.events" :key="event.id" :event="event"/>
        ...
    </template>
    <script>
        ...
        hasNextPage() {
          return this.event.eventsTotal > this.page * this.perPage
        },
        ...mapState(['event', 'user'])
      }
    }
    </script>

 

/src/views/EventShow.vue

Using our event object all over the place, Instead of writing event.event.time. There’s another way to use the mapState helper.

event.event.time을 작성하는 대신 이벤트 객체를 여기저기서 사용합니다. mapState 도우미를 사용하는 또 다른 방법이 있습니다.

Mapping Component computed property called event to the event state in our event module.

이벤트라는 Component computed 속성을 이벤트 모듈의 이벤트 상태에 매핑합니다.

 

      ...
      computed: mapState({
        event: state => state.event.event
      })

Alternate Syntax for Modules

The first is preferable, as it’s easier to create private variables and methods.

개인 변수와 메서드를 만드는 것이 더 쉽기 때문에 첫 번째 방법이 더 좋습니다.

 

/src/store/store.js

    import * as event from '@/store/modules/event.js'
    ...

/src/store/modules/event.js

    import EventService from '@/services/EventService.js'
    
    export const state = { ... }
    export const mutations = { ... }
    export const actions = { ... }
    export const getters = { ... }

below is the alternate syntax.

 

/src/store/store.js

    import event from '@/store/modules/event.js'
    ...

 

/src/store/modules/event.js

    import EventService from '@/services/EventService.js'
    
    export default {
      state: { ... },
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }

 

Accessing State in Other Modules

In order to get access to our user’s name, use rootState, which gives me access to the user module.

사용자 이름에 접근하려면 rootState를 사용하세요. 이를 통해 사용자 모듈에 접근할 수 있습니다.

In the same way,, access rootGetters if there are Getters in a different module.

같은 방법으로 다른 모듈에 Getter가 있으면 rootGetters에 액세스합니다.

        createEvent({ commit, rootState }, event) {
        
          console.log('User creating Event is ' + rootState.user.user.name)
          
          return EventService.postEvent(event).then(() => {
            commit('ADD_EVENT', event)
          })
        },

 

Accessing another Module’s Actions

Simply send in dispatch from the context object and call that action’s name.

간단히 컨텍스트 object 에서 디스패치에서 ​​보내고 해당 action’s name 을 지정하면 됩니다.

Don’t need to mention what module actionToCall is in, because by default all our actions, mutations, and getters are located in the Global NameSpace.

기본적으로 모든 actions , mutations 및 getter가 Global NameSpace에 있으므로 actionToCall이 어떤 모듈에 있는지 언급할 필요가 없습니다.

        createEvent({ commit, dispatch, rootState }, event) {
        
          console.log('User creating Event is ' + rootState.user.user.name)
          
          dispatch('actionToCall')
          
          return EventService.postEvent(event).then(() => {
            commit('ADD_EVENT', event)
          })
        },

 

Understanding the Global NameSpace

Actions, Mutations, and Getters (even inside modules) are all registered under the global namespace.

Actions, Mutations, Getters(모듈 내부 포함)는 모두 전역 네임스페이스에 등록됩니다.

The downfall of this implementation is that we could end up with naming collisions..hence should ensure your action, mutation, and getters never conflict.

이 구현의 단점은 naming collisions 충돌로 끝날 수 있다는 것입니다. 따라서 action , mutation 및 getter가 충돌하지 않도록 해야 합니다.

 

NameSpacing our Modules

Make your modules to be more self-contained, reusable, and perhaps avoid accidentally having two modules that are the same name.

모듈을 더욱 독립적이고 재사용 가능하게 만들고 실수로 이름이 같은 두 개의 모듈이 생기는 것을 방지할 수 있습니다.

 

/src/store/modules/event.js

    import EventService from '@/services/EventService.js'
    
    export const namespaced = true
    
    export const state = {
      events: [],
        ...

With this one line of configuration, our Getters, Mutations, and Actions now must be addressed using this namespace.in our EventList.vue:

이 한 줄의 configuration으로 이제 EventList.vue에서 이  네임스페이스를 사용하여 Getter, Mutations 및 Action을 처리해야 합니다.

    this.$store.dispatch('fetchEvents', { ... })

becomes

this.$store.dispatch('event/fetchEvents', { ... })

 

Small Aside about mapActions

Allows to map component methods to store.dispatch calls.

component  메서드를 store.dispatch 호출에 매핑할 수 있습니다.

 

/src/views/EventShow.vue

    import { mapState } from 'vuex'
    
    export default {
      props: ['id'],
      created() {
        this.$store.dispatch('event/fetchEvent', this.id)
      },
      computed: mapState({
        event: state => state.event.event
      })
    }

becomes below codes

    import { mapState, mapActions } from 'vuex'
    
    export default {
      props: ['id'],
      created() {
        this.fetchEvent(this.id)
      },
      computed: mapState({
        event: state => state.event.event
      }),
      methods: mapActions('event', ['fetchEvent'])
    }

The first argument to mapActions here is the namespace, and the second is an array of methods we want our component to have an alias to.

여기서 mapActions의 첫 번째 인수는 네임스페이스이고, 두 번째 인수는 component에 별칭을 부여할 메서드 배열입니다.

 

Accessing NameSpaced Getters

Although we don’t access any of our getters from our current code, in our state & getter lesson we had the following code:

현재 코드에서는 어떤 게터에도 액세스하지 않지만 상태 및 게터 강의에서는 다음 코드를 사용했습니다.

    computed: {
      getEventById() {
         return this.$store.getters.getEventById
      }
    }

Which we simplified using mapGetters :

mapGetters를 사용하여 단순화했습니다.

 

 computed: mapGetters(['getEventById'])

Access our namespaced getter method inside our event module.

이벤트 모듈 내에서 네임스페이스가 지정된 getter 메서드에 액세스합니다.

    computed: {
      getEventById() {
        return this.$store.getters['event/getEventById']
      }
    }

be shortened to

 computed: mapGetters('event', ['getEventById'])

Does any of this code change with NameSpaced Modules?

        createEvent({ commit, dispatch, rootState }, event) {
        
          console.log('User creating Event is ' + rootState.user.user.name)
          
          dispatch('actionToCall')
          
          return EventService.postEvent(event).then(() => {
            commit('ADD_EVENT', event)
          })
        },

rootState.user.user.name : is correct

dispatch('actionToCall') : Only needs to change if the action it’s trying to call is not inside this module.

dispatch('actionToCall') :  호출하려는 작업이 이 모듈 내부에 없는 경우에만 변경하면 됩니다.

commit('ADD_EVENT', event) : Only needs to change if the mutation it’s trying to is not inside this module.

시도 중인 mutation 가 이 모듈 내부에 없는 경우에만 변경하면 됩니다.

 

How do I call an Action inside of an Action?

There may be times where you need to call an action inside of another action.

다른 action 내에서 action을 호출해야 하는 경우가 있을 수 있습니다.

If your action is inside of your current module (or you’re not using namespaced modules) you can just do

action이 현재 모듈 내부에 있는 경우(또는 네임스페이스 모듈을 사용하지 않는 경우) 다음을 수행할 수 있습니다.

 

/src/store/modules/event.js

    ...
     actions: {
        createEvent({ commit, dispatch }, event) {
          ...
          dispatch('actionToCall')
        }
    ...

 

What if the action I want to call is in another module which is namespaced?

/src/store/modules/event.js

need to use the action’s module name, provide a payload as the second argument (null if there is none), and pass { root: true } as the third argument.

작업의 모듈 이름을 사용해야 하고, 페이로드를 두 번째 인수로 제공하고(없으면 null), 세 번째 인수로 { root: true } 를 전달해야 합니다.

    ...
     actions: {
        createEvent({ commit, dispatch }, event) {
          ...
          dispatch('moduleName/actionToCall', null, { root: true })
        }
    ...

 

Resources

https://vuex.vuejs.org/guide/modules.html

 

Modules | Vuex

Modules Due to using a single state tree, all states of our application are contained inside one big object. However, as our application grows in scale, the store can get really bloated. To help with that, Vuex allows us to divide our store into modules. E

vuex.vuejs.org

https://github.com/Code-Pop/real-world-vue/releases/tag/lesson14-modules-finish

 

Release lesson14-modules-finish · Code-Pop/real-world-vue

NameSpace Event

github.com

 

'Java Scripts > Vue.js' 카테고리의 다른 글

Vue - Global and Per-Route Guards  (0) 2022.05.14
Vuex : Success & Error Notifications  (0) 2022.05.06
Vue : In-Component Route Guards  (0) 2022.05.01
Vue : Flash Messages  (0) 2022.05.01
Vue : Programmatic Navigation  (0) 2022.04.30