익명의 개발노트

[typescript] vuex 본문

코딩일기/TIL

[typescript] vuex

캡틴.JS 2020. 1. 5. 20:19
반응형

1. 인터페이스를 이용한 설계 방식

   1) 인터페이스에서 state의 타입을 지정해준다.

   2) 스토어 타입을 지정한다. 스토어 타입은 Vuex에 StoreOptions라는 내장 객체타입을 이용한다.

   3) Actions의 타입은 ActionContext타입을 사용하며, 들어가는 제너릭 타입은 State, RootState 인데,

      아래 예제는 하나밖에 없으므로 RootState대신 State로 지정해주어도 무방하다.

import Vue from 'vue';
import Vuex, { StoreOptions, ActionContext } from 'vuex';

Vue.use(Vuex);

//1.인터페이스 타입지정
interface State {
  count: number;
}

//2. 스토어 타입지정, interface로 선언한 부분은 구현을 반드시해야함. 
const store: StoreOptions<State> = {
  state: {
    count: 0, //위에서 구현했기에 초기값 반드시 넣어주어야함.
  },
  mutations: {
    setCount(state: State, count: number) {
      state.count = count;
    },
  },
  actions: {
    //첫번째 객체는 스토어에서 사용하는 객체 다 사용가능(사용할꺼만 넣어주면됨)
    //ActionContext 타입의 두번째 타입은 RootState임. 현재는 State로 해도됨.
    //두번째 인자는 액션함수의 파라미터임.
    increase({state, getters, dispatch, commit}: ActionContext<State, State>) {
      commit('setCount', state.count + 1);
    },
    decrease({state, commit}: ActionContext<State, State>) {
      commit('setCount', state.count - 1);
    },
  },
  getters: {
    count: (state: State) => state.count,
  },

};

export default new Vuex.Store(store);

  3) Module를 이용한 방식

      (1) 모듈은 어플리케이션 규모가 커짐에 따라 Store에서 관리하는 규모도 같이 커지게 되는데, 효율적인 관리를 위해 모듈화한다.

      (2) moduleA, moduleB를 모듈화로 나누어 관리하는 예제이다.

//index.ts

import Vue from 'vue';
import Vuex, { StoreOptions, ActionContext } from 'vuex';
import moduleA from '@/store/moduleA.store';
import moduleB from '@/store/moduleB.store';


Vue.use(Vuex);

export interface RootState {
  data: string;
}

const store: StoreOptions<RootState> = {
  state: {
    data: 'root',
  },
  modules: {
    moduleA,
    moduleB,
  },
  mutations: {
    setData(state, data: string) {
      state.data = data;
    },
  },
  actions: {
    //ActionContext 타입이 RootState이므로 위에 있으니 따로 지정안해줘도 됨.
    //기본적으로 RootState에서 관리하기 때문에 각 모듈별로 네임스페이스를 지정해줘야 해당 모듈만 데이터 변화가능.
    setRootData ({commit}, data: string) {
      commit('setData', data);
    },
  },
  getters: {
    data: (state) => state.data, 
  },
}

export default new Vuex.Store(store);
//moduleA

import {Module} from 'vuex';
import {RootState} from '@/store/index';

interface moduleA {
    data : string;
}

const module: Module<moduleA, RootState> = {
    namespaced: true, // 지정을 해줘야 RootState와 따로 동작한다. 없으면, RootState 변경시 같이 변경
    state: {
        data: 'moduleA',
    },
    //RootState와 똑같이 작성해도 된다.
    mutations: {
        setData(state, data: string) {
          state.data = data;
        },
    },
    actions: {
      setRootData ({commit}, data: string) {
        commit('setData', data);
      },
    },
    getters: {
      data: (state) => state.data, 
    },
}

export default module;
//moduleB

import {Module} from 'vuex';
import {RootState} from '@/store/index';

interface moduleB {
    data : string;
}

const module: Module<moduleB, RootState> = {
    namespaced: true,
    state: {
        data: 'moduleB',
    },
    mutations: {
        setData(state, data: string) {
          state.data = data;
        },
      },
    actions: {
      setRootData ({commit}, data: string) {
        commit('setData', data);
      },
    },
    getters: {
      data: (state) => state.data, 
    },
}

export default module;

   (3) rootState, moduleA State, modulB State 3가지로 나뉘는데, 네임스페이스를 지정하지 않을 경우 RootState 데이터 변경시 네임스페이스를 지정하지 않은 모듈의 데이터를 전부 바꾼다.

   (4) App.vue에서 console.log(this.$store)를 해보면,

console.log(this.$store)

_actions 밑에 네임스페이스로 구분된 데이터들이 명시되어있다. 네임스페이스를 지정하지 않으면, setRootData만 보인다.

//App.vue

<template>
  <div id="app">
   {{ $store.state}}
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component({
  components: {
  },
})
export default class App extends Vue {
  created(){
    console.log(this.$store);
    this.$store.dispatch('setRootData', 'test'); //RootState
    this.$store.dispatch('moduleB/setRootData', 'test'); //ModuleState
  }
}
</script>

RootState 또는 모듈 state 값을 변경하고자 하면 dispatch의 첫번째 인자값에 _actions 밑에 명시된 객체이름을 넣어주면 된다.

2. 클래스를 이용한 설계방식   

  1) vuex-module-decorators 

npm install vuex-module-decorators

   (1) 위에서 작성한 코드 중에서 ModuleB만 아래와 같이 변경

import {Module, VuexModule, Mutation, Action} from 'vuex-module-decorators';

@Module({namespaced: true, name: 'moduleB'})
export default class ModuleB extends VuexModule {
    data: string = 'moduleB';
    
    //class안에서는 인자 1개
    @Mutation
    setData(data: string){
        this.data = data;
    }
    //mutation하고 같은 범위라 이름 안겹치게.
    @Action
    editData(data: string) {
        this.context.commit('setData', data);
    }

    get moduleBdata(){
        return this.data;
    }
}

2) vuex-class

npm install vuex-class
//기존코드

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import Count from '@/components/Count.vue';
import {Action} from 'vuex-class';

@Component({
  components: {
      Count,
   }
})
export default class App extends Vue {
  increase(){
    this.$store.dispatch('increase');
  };
  decrease(){
    this.$store.dispatch('decrease');

  };
}
</script>

위 코드를 vuex-class를 설치한 후 데코레이터를 이용해서 아래와 같이 변경가능하다. 굉장히 간단해 진다.

//vuex-class 적용한 코드

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import Count from '@/components/Count.vue';
import {Action, Mutation} from 'vuex-class';

@Component({
  components: {
      Count,
   }
})
export default class App extends Vue {
  @Action readonly increase;
  @Action readonly decrease;

  @Mutation readonly setCount;
}
</script>
반응형
Comments