Today I Learned 50 - Vuex

angie·2022년 11월 8일

Vue.js

목록 보기
8/12
post-thumbnail

1. Vuex?

  • Vue.js 애플리케이션에 대한 상태 관리 패턴 + 라이브러리
  • 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소 역할을 하며 예측 가능한 방식으로 상태를 변경

상태 관리 패턴이란?

  • Vuex에서 상태(state)는 Vue의 data 속성과 유사
  • 상태관리란 여러 컴포넌트 간의 데이터 전달과 이벤트 통신을 한 곳에서 관리하는 패턴
  • 여러 컴포넌트가 같은 상태(state, 즉 data)를 유지

상태 관리 패턴이 필요한 이유

  • Vue에서 컴포넌트끼리 data를 주고 받을 때, 'Pass Props'와 'Emit Event' 기능을 사용했다.
  • 하지만 컴포넌트의 중첩이 깊어지면 데이터 전달이 쉽지 않다.
  • 해결방법: 여러 컴포넌트가 공유하고 있는 상태(data)를 한 곳에 저장하여, 컴포넌트의 계층에 상관없이 데이터에 접근한다.

2. Vue CLI에서 Vuex 시작하기

1) 프로젝트 생성

$ vue create vuex-app  // vue 프로젝트 생성
$ cd vuex-app  // 디렉토리 이동
$ vue add vuex  // Vue CLI를 통해 vuex plugin 적용

vsc의 bash에서 위의 명령어로 Vue 프로젝트를 생성하고 Vuex를 추가하면, 아래와 같은 경고문이 나온다.

yes를 선택하고 진행해도 무방하다.

프로젝트 구조


src/store/index.js가 vuex instance를 정의하는 곳이다.

2) Vuex 핵심 컨셉

// vuex instance 구조
export default new Vuex.Store({
  // 1. state
  state: {
  },
  // 2. getters
  getters: {
  },
  // 3. mutations
  mutations: {
  },
  // 4. actions
  actions: {
  },
  modules: {
  },
})

(1) state

중앙 저장소에서 관리하는 모든 상태 정보

단일 상태 트리

  • Vuex는 단일 상태 트리를 사용
  • 단일 상태 트리를 사용한다는 것은 각 애플리케이션마다 단 하나의 저장소를 갖는 것

Vuex 상태를 Vue 컴포넌트에서 가져오기

  • 컴포넌트의 computed에서 $store.state로 state에 접근하여 계산된 속성을 정의 후 사용 (권장방법)

예시1 : template에서 바로 state에 접근

// src/store/index.js
export default new Vuex.Store({
  state: {
    message: 'message in store'
  },
  ...
})
// app.vue
<template>
  <div id="app">
    <h1>{{ $store.state.message }}</h1>
  </div>
</template>

예시2 : computed에 정의 후 state 사용

// src/store/index.js
export default new Vuex.Store({
  state: {
    message: 'message in store'
  },
  ...
})
// app.vue
<template>
  <div id="app">
    <h1>{{ message }}</h1>
  </div>
</template>

<script>
export default {
	...
  computed: {
    message() {
      return this.$store.state.message
    }
  }
}
</script>

(2) getters

  • 저장소의 state를 기반으로 계산된 값을 얻고자 할 때 사용한다.
  • Vue 인스턴스의 computed와 마찬가지로, 일부 종속되어있는 값이 변경된 경우에만 재계산된다.
  • getters에 작성되는 모든 메서드는 첫 번째 전달인자로 state를 받는다.
  • 두 번째 전달인자로는 getters를 받는다.
// 예시1
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    // state를 첫번째 인자로 받는다.
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})
// 예시2
getters: {
  // 두번째 인자로 getters를 받는다.
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

속성값 유형의 getters에 접근

// store/index.js
getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}
// 컴포넌트에서 값에 접근
computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

메서드 유형의 getters에 접근

  • 함수를 반환하여 getter에 전달인자를 전달할 수 있다.
  • 저장소의 배열을 검색할 때 유용하다.
// store/index.js
getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

(3) mutations

  • state를 변경하기 위한 메소드를 정의하는 곳
  • mutations에 정의된 함수들은 핸들러 함수라 부른다.
  • mutations의 함수는 반드시 동기적이야한다. (비동기로 하게 되면 state의 변화 시기를 특정할 수 없기 때문)
  • 첫 번째 인자로 state를 받는다.
  • mutations의 핸들러를 호출하려면 store.commit을 호출

payload

store.commit에 payload를 추가 전달인자를 사용할 수 있다.

// ...
mutations: {
  // 두번째 인자 n이 payload를 받는 매개변수
  increment (state, n) {
    state.count += n
  }
}
// payload를 10으로 보내고 있다.
store.commit('increment', 10)

Object-style commit

payload는 여러 필드를 포함하는 객체로 보낼 수 있다.

// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

mutations 이름 상수로 하기

  • mutations를 지정하는 상수를 단일 파일에 저장하면 공동 작업자가 전체 애플리케이션에서 어떤 변이가 가능한지 한눈에 파악 가능하다.
  • 대규모 프로젝트시 유용 (프로젝트에 따라 필수가 아닌 선택)
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // ES2015에서 계산 된 프로퍼티 이름 기능을 사용하여
    // 상수를 함수 이름으로 사용할 수 있습니다
    [SOME_MUTATION] (state) {
      // 변이 상태
    }
  }
})

(4) actions

  • context 객체를 첫번째 인자로 받는다.

actions가 mutations와 다른 점은 아래와 같다.

  • state를 변경할 때 actions안에서 mutations에 대한 commit을 한다.
  • actions에서는 비동기 작업이 있을 수 있다.

context

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

context 객체는 store 인스턴스의 모든 메소드와 프로퍼티를 가지고 있는 객체이다.

  • context.commit : mutations를 커밋
  • context.state : state에 접근
  • context.getters : getters에 접근

dispatch

  • actions는 store.dispatch 메소드를 통해 호출
dispatch(A, B)
  • A : 액션 함수 이름
  • B : 넘겨주는 데이터 (payload)
예시
store.dispatch('increment')

payload로 함께 dispatch로 액션을 사용할 수 있다.

// 페이로드와 함께 디스패치
store.dispatch('incrementAsync', {
  amount: 10
})

// 객체와 함께 디스패치
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

컴포넌트 내부에서 dispatch 사용

this.$store.dispatch('actions-name')을 사용하여 컴포넌트에서 액션을 호출

// App.vue
<template>
  <div>
  	<input type="text" @keyup.enter="changeMessage" v-model="inputData">
  </div>
</template>
<script>
export default {
	// ...
  	data() {
      	return {
        	inputData: null,
        }
    },
  	methods: {
    	changemessage() {
          	const newMessage = this.inputData
			this.$store.dispatch('changeMessage', newMessage)
        }
    }
}
</script>
// store/index.js
export default new Vuex.Store({
	...
  	actions: {
    	changeMessage(context, message) {
        	// context, message(payload) 사용
        },
    }
})
profile
better than more

0개의 댓글