[Vue] Vuex 본문

Web/Vue

[Vue] Vuex

미니모아 2022. 9. 13. 08:55
반응형

Vuex

global state를 관리하는 라이브러리

local state는 하나의 컴포넌트에만 영향을 미치지만 global state는 어플리케이션 전체에 영향을 미칠 수 있다.

why

  • global state를 관리하는 것은 어려울 수 있다.

    • 하나의 컴포넌트에 너무 많은 로직과 데이터를 포함하게 된다.

    • 데이터의 변화를 예측할 수 없다.

    • 에러를 일으킬 수 있고 추적이 어렵다.

Using store

main.js에 createStore를 통해 store를 생성한 후 app에 연결한다.

import { createApp } from 'vue';
import { createStore } from 'vuex';

import App from './App.vue';



const store = createStore({
  state() {
    return {
      counter: 0,
    };
  }
});

const app = createApp(App);

app.use(store);
app.mount('#app');

다른 컴포넌트들에서는 $store.state로 접근해서 값을 가져올 수 있다.

<template>
  <base-container title="Vuex">
    <h3>{{$store.state.counter}}</h3>
    <button>Add 1</button>
  </base-container>
</templa  te>

##

Mutations

vuex는 state을 컴포넌트에 제공하지만 컴포넌트에서 직접 state를 변경하는 것은 이상적이지 않다. 각각 다른 컴포넌트에서 동시에 state를 변경할 경우 값을 추적하기 힘들기 때문이다. vuex는 mutations을 이용해 state의 변경이 모든 컴포넌트에서도 동일하게 이뤄지는 것을 보장한다.

store에 mutationd을 정의하고 빌드인 메소드 commit을 통해 호출할 수 있다.

const store = createStore({
  state() {
    return {
      counter: 0,
    };
  },
  mutations: {
    increment(state) {
      state.counter = state.counter + 2;
    }
  }
});
export default {
    methods: {
      addOne() {
        this.$store.commit('increment');
      }
    }
  }

payload

payload 객체를 이용해 mutaion으로 인수를 넘길 수 있다.

 increase(state, payload) {
      state.counter = state.counter + payload.value;
    }

두 가지 방식으로 해당 mutation을 호출할 수 있다.

this.$store.commit('increase', {value: 10});

this.$.commit({
    type: 'increase',
    value: 10,
})

Getter

state를 직접 읽어오는 것도 이상적인 방식이 아니다. state의 값을 변경해서 가져오고 싶을 때 해당 state를 참조하는 모든 컴포넌트에 각각 computed를 작성해줘야하므로 일관성이 떨어질 수 있다.

이를 위해 Getter를 사용한다.

getter가 받는 인자

  • state

  • getters

 getters: {
    finalCounter(state) {
      return state.counter * 2;
    }

각 컴포넌트에는 getter로 접근해 computed state를 가져올 수 있다.

this.$store.getter.finalCounter;

getter는 다른 getters의 값을 가져와 쓸 수 있다.

 normalizedCounter(_, getters) {
      const finalCounter = getters.finalCounter;
      if (finalCounter < 0) {
        return 0;
      }
      if (finalCounter > 100) {
        return 100;
      }
      return finalCounter;
    }

Action

Mutations 는 반드시 동기로 처리되어야한다. state 값이 변경되는 도중에 비동기 처리를 위해 멈춘다면 에러가 발생할 것이다. 따라서 비동기 처리를 위해서는 mutation에서 component로 가는 사이에 Actions 을 거친다.

actions: {
    increment(context) {
      setTimeout(() => {
        context.commit('increment')
      }, 2000)
    },
    increase(context, payload) {
      setTimeout(() => {
        context.commit('increment', payload)
      }, 2000)
    }
  },

action은 dispatch로 가져와 쓸 수 있다.

this.$store.dispatch({
        type: 'increase',
        value: 10,
      })

Mapper

MapGetter

mapGetter로 원하는 computed state를 한꺼번에 가져올 수 있다.

<script>
  import { mapGetters } from 'vuex';

  export default {
    computed: {
      ...mapGetters(['finalCounter'])
    },
  }
</script>

MapActions

action을 한꺼번에 가져온다

  methods: {
      ...mapActions(['increase', 'increment'])
    }

payload는 인자로 넘기면 된다.

<template>
  <Button @click="increase({value: 10})">add 10</Button>
  <Button @click="increment">add 10</Button>
</template>

배열이 아닌 객체 형태로 키와 action을 매핑할 수도 있다.

Module

기능별로 모듈을 나눈 후 스토어에 등록하여 사용할 수 있다.

이 때 각 모듈의 state는 local이다.

const counterModule = {
  state() {
    return {
      counter: 0,

    }
  },
  mutations: {
    increment(state) {
      state.counter++
    },
    increase(state, payload) {
      state.counter += payload.value
    },
  },
  actions: {
    increment(context) {
      setTimeout(() => {
        console.log(context)
        context.commit('increment')
      }, 2000)
    },
    increase(context, payload) {
      setTimeout(() => {
        console.log(context)
        context.commit('increase', payload)
      }, 2000)
    },
  },
  getters: {
    finalCounter(state) {
      return state.counter * 3;
    },
    normalizedCounter(_, getters) {
      const finalCounter = getters.finalCounter;
      if (finalCounter < 0) {
        return 0;
      }
      if (finalCounter > 100) {
        return 100;
      }
      return finalCounter;
    },
  }
};

const store = createStore({
  modules: {
    numbers: counterModule
  },
  state: {
    isLoggedIn: false,
  },
  mutations: {
    setAuth(state, payload) {
      state.isLoggedIn = payload.isAuth
    }
  },
  actions: {
    login(context) {
      context.commit('setAuth', {isAuth: true});
    },
    logout(context) {
      context.commit('setAuth', {isAuth: false});
    }
  },
  getters: {
    userIsAuthenticated(state) {
      return state.isLoggedIn
    }
  },
})

메인 스토어의 state와 getter에 접근하려면 rootState, rootGetters를 쓴다.

testAuth(state, getters, rootState, rootGetters) {
      console.log(state, getters, rootState, rootGetters)
      return rootState.isLoggedIn
    },

namespaced

namespace를 지정하면 해당 모듈의 getter,state 등을 호출할 때 namespace를 붙여야한다. 이 때 namespace는 메인 스토어에 모듈을 등록할 때 사용한 키값이 된다.

const counterModule = {
  namespaced: true,
// (....)
}
const store = createStore({
  modules: {
    numbers: counterModule
  },
// (...)
}
...mapGetters('numbers', ['finalCounter'])
this.$store.commit('numbers/increment')
this.$store.getters['numbers/normalizedCounter'];
..mapActions('numbers', {
        inc: 'increment',
        inr: 'increase'
      })
반응형

'Web > Vue' 카테고리의 다른 글

[Vue] Options API => Composition API  (0) 2022.10.21
[Vue] Router  (0) 2022.09.13
[Vue] v-model  (0) 2022.08.11
[Vue] 뷰 CLI에서 사용하는 NPM  (0) 2022.08.08
[Vue] Vuex, 뷰의 반응성, SSR, 뷰 개발을 위한 웹 팩  (0) 2022.08.08
Comments