WEB/Vue.js

[Vue.js] Vuex

Raymond 2022. 8. 13. 20:52

Vuex


Vuex는 Vue.js 애플리케이션을 위한 상태 관리 패턴 + 라이브러리 입니다. 모든 컴포넌트를 위한 중앙 집중식 저장소를 제공해 줍니다. 그렇기 때문에 중앙 저장소에 저장된 데이터는 쉽게 바꿀 수 있는게 아니고, 특정 함수(mutations, actions)를 통해서만 데이터 변경이 가능하게하고 변경 상태를 관리할 수 있습니다.

 

Vuex 사용법

state, getter, mutations, actions, modules로 상태를 관리합니다.

state : vue에서의 data영역과 같은 역할을 합니다.
getter : vue에서의 computed영역과 같은 역할을 합니다.
mutations : state의 데이터를 바꿀수 있는 유일한 역할을 합니다.
actions : mutations 하고 비슷한데, mutations에 정의한 함수를 커밋(commit)시켜서 state를 변경 + 비동기 처리가 가능
modules : 용도에 맞게 js파일을 나누어서 모듈화하여 사용을 하는 경우 사용이 된다.

기본구조

import { createStore } from 'vuex'

export default createStore({
  state() {
    return {
      todos: [
        { id: 1, title: 'todo 1', done: true },
      ]
    }
  },
  getters: {
    doneTodosCount(state) {
      return state.todos.filter((todo) => todo.done).length
    }
  },
  mutations: {
    doneYN(state, doneStatus) {
      state.todos.filter((todo) => todo.id === doneStatus.id)[0].done =
        doneStatus.done
    }
  },
  actions: {
    add({ commit }, item) {
      // const {commit, state} = context
      // context.commit, context.state

      // 서버랑 통신. fetch, axios
      commit('add', item)
    }
    // add2: async ({ commit }, item) => {
    //   await fetch('', {})
    // }
  },
  modules: {}
})

todo 예제

폴더구조

project
|--src
    |--store
    |	|--index.js
    |	|--todo.js    
    |--views
    |	|--vuex
    |       |--TodoView.vue
    |--main.js

 

index.js

import { createStore } from 'vuex'
import { todo } from './todo'
// 새로고침 해도 영구적으로 데이터가 남아있게 만드는 모듈
import persistedstate from 'vuex-persistedstate'

export default createStore({
  modules: {
    todo: todo
  },
  plugins: [persistedstate({ paths: ['todo.todos'] })]
})
persistedstate 모듈
데이터를 도메인 Local Storage에 저장을 해주는 모듈
사용처 : 장바구니, 마직막 접속페이지

vue cookies 모듈
유저의 데이터와 같이 영구적이지 않지만 일정시간이후에 데이터를 날려야 하는 경우 사용되는 모듈
사용처 : 유저 데이터(로그인시)

 

persistedstate 모듈
vue cookies 모듈

 

todo.js

export const todo = {
  // namespaced를 true로 해줘야 index.js에서 moduls에 todo를 넣을수 있다.
  namespaced: true,
  state() {
    return {
      todos: [
        { id: 1, title: 'todo 1', done: true },
        { id: 2, title: 'todo 2', done: false },
        { id: 3, title: 'todo 3', done: false }
      ]
    }
  },
  getters: {
    todosCount(state) {
      return state.todos.length
    },
    doneTodosCount(state) {
      return state.todos.filter((todo) => todo.done).length
    },
    notDoneTodosCount(state) {
      return state.todos.filter((todo) => !todo.done).length
    }
  },
  mutations: {
    add(state, item) {
      state.todos.push(item)
    },
    remove(state, id) {
      state.todos = state.todos.filter((todo) => todo.id !== id)
    },
    doneYN(state, doneStatus) {
      state.todos.filter((todo) => todo.id === doneStatus.id)[0].done =
        doneStatus.done
    }
  },
  actions: {
    add({ commit }, item) {
      commit('add', item)
    }
  }
}

 

TodoView.vue

<template>
  <div>
    <div>{{ todos }}</div>
    <div>할일 목록 전체수 : {{ todosCount }}</div>
    <div>완료 목록 수 : {{ doneTodosCount }}</div>
    <div>미완료 목록 수 : {{ notDoneTodosCount }}</div>
    <div>
      <label for="todo" class="form-label">할일</label>
      <input
        type="text"
        name=""
        id="todo"
        class="form-control"
        v-model="todo"
      />
      <button class="btn btn-primary" @click="addItem">추가(mutation)</button>
      <button class="btn btn-primary" @click="addItem2">추가(action)</button>
    </div>
    <div>
      <table class="table table-bordered table-striped">
        <thead>
          <tr>
            <th>Todo ID</th>
            <th>할일</th>
            <th>완료여부</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr :key="todo.id" v-for="todo in todos">
            <td>{{ todo.id }}</td>
            <td>{{ todo.title }}</td>
            <td>
              <div class="form-check form-switch">
                <input
                  class="form-check-input"
                  type="checkbox"
                  role="switch"
                  id="flexSwitchCheckChecked"
                  :checked="!todo.done"
                  @change="doneYN(todo.id, $event)"
                />
              </div>
            </td>
            <td>
              <button
                class="btn btn-danger btn-sm"
                @click="removeItem(todo.id)"
              >
                삭제
              </button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>
<script>
export default {
  components: {},
  data() {
    return {
      todo: ''
    }
  },
  computed: {
    todos() {
      return this.$store.state.todo.todos
    },
    todosCount() {
      return this.$store.getters['todo/todosCount']
    },
    doneTodosCount() {
      return this.$store.getters['todo/doneTodosCount']
    },
    notDoneTodosCount() {
      return this.$store.getters['todo/notDoneTodosCount']
    }
  },
  setup() {},
  created() {},
  mounted() {},
  unmounted() {},
  methods: {
    addItem() {
      this.$store.commit('todo/add', { id: 4, title: this.todo, done: false })
    },
    removeItem(id) {
      this.$store.commit('todo/remove', id)
    },
    doneYN(id, event) {
      this.$store.commit('todo/doneYN', { id: id, done: !event.target.checked })
    },
    addItem2() {
      // dispatch로 actions에 접근가능
      this.$store.dispatch('todo/add', { id: 4, title: this.todo, done: false })
    }
  }
}
</script>

output

 

main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

const app = createApp(App)
app.use(store)
app.use(router)
app.mount('#app')

 

참고자료

https://vuex.vuejs.org/