VueX에 어떤 기능이 추가되나요?
게시 됨: 2022-03-10Vuex는 Vue 애플리케이션의 상태 관리를 위한 솔루션입니다. 다음 버전인 Vuex 4는 공식적으로 출시되기 전에 최종 단계를 진행하고 있습니다. 이 릴리스는 Vue 3와 완벽하게 호환되지만 새로운 기능을 추가하지는 않습니다. Vuex는 항상 강력한 솔루션이었으며 Vue의 상태 관리를 위한 많은 개발자의 첫 번째 선택이었지만 일부 개발자는 더 많은 워크플로 문제가 해결되기를 바랐습니다. 그러나 Vuex 4가 출시된 지 얼마 되지 않은 시점에서 Kia King Ishii(Vue 핵심 팀원)가 Vuex 5에 대한 그의 계획에 대해 이야기하고 있으며, 제가 본 것에 대해 너무 흥분해서 여러분과 공유하게 되었습니다 모두. Vuex 5 계획이 확정 되지 않았 으므로 Vuex 5가 출시되기 전에 몇 가지 사항이 변경될 수 있지만 이 기사에서 보는 것과 거의 비슷하다면 개발자 경험이 크게 개선될 것입니다.
Vue 3와 구성 API의 출현으로 사람들은 손으로 만든 간단한 대안을 찾고 있습니다. 예를 들어, You Might Not Need Vuex 는 공유 상태 저장소를 생성 provide/inject
과 함께 구성 API를 사용하기 위한 비교적 단순하지만 유연하고 강력한 패턴을 보여줍니다. 그러나 Gabor가 그의 기사에서 언급했듯이 이(및 기타 대안)은 커뮤니티 지원, 문서, 규칙, 우수한 Nuxt 통합 및 개발자와 같이 코드와 직접 관련이 없는 모든 것이 부족하기 때문에 소규모 애플리케이션에서만 사용해야 합니다. 도구.
마지막 문제는 항상 나에게 가장 큰 문제 중 하나였습니다. Vue devtools 브라우저 확장은 항상 Vue 앱을 디버깅하고 개발하기 위한 놀라운 도구였으며 "시간 여행"으로 Vuex 검사기를 잃는 것은 중요하지 않은 응용 프로그램을 디버깅하는 데 상당한 손실이 될 것입니다.
고맙게도 Vuex 5를 사용하면 케이크도 먹고 먹을 수 있습니다. 이러한 구성 API 대안과 더 유사하게 작동하지만 공식 상태 관리 라이브러리 사용의 모든 이점을 유지합니다. 이제 어떤 변화가 있을지 살펴보겠습니다.
스토어 정의
Vuex 스토어로 무엇이든 하기 전에 정의해야 합니다. Vuex 4에서 저장소 정의는 다음과 같습니다.
import { createStore } from 'vuex' export const counterStore = createStore({ state: { count: 0 }, getters: { double (state) { return state.count * 2 } }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })
각 저장소에는 네 부분이 있습니다. state
는 데이터를 저장하고, getters
는 계산된 상태를 제공하고, mutations
는 상태를 변경하는 데 사용되며, actions
은 저장소와 관련된 모든 작업을 수행하기 위해 저장소 외부에서 호출되는 메서드입니다. 일반적으로 작업은 이 예제에서 볼 수 있는 것처럼 돌연변이를 커밋하지 않습니다. 대신 돌연변이는 동기식 이어야 하거나 더 복잡하거나 다단계 기능을 구현하기 때문에 비동기식 작업을 수행하는 데 사용됩니다. 또한 액션은 자체적으로 상태를 변경할 수 없습니다. 그들은 mutator를 사용해야 합니다. Vuex 5는 어떻게 생겼습니까?
import { defineStore } from 'vuex' export const counterStore = defineStore({ name: 'counter', state() { return { count: 0 } }, getters: { double () { return this.count * 2 } }, actions: { increment () { this.count++ } } })
여기에 몇 가지 변경 사항이 있습니다. 먼저 createStore
대신 defineStore
를 사용합니다. 이 차이는 무시할 수 있지만 의미론적 이유 때문에 존재합니다. 이에 대해서는 나중에 살펴보겠습니다. 다음으로 이전에는 필요하지 않았던 상점 name
을 제공해야 합니다. 과거에는 모듈에 고유한 이름이 있었지만 모듈 자체에서 제공하지 않았습니다. 그것들을 추가한 상위 상점에서 할당한 속성 이름일 뿐입니다. 이제 모듈이 없습니다 . 대신 각 모듈은 별도의 저장소가 되며 이름이 있습니다. 이 이름은 나중에 이야기할 Vuex 레지스트리에서 사용됩니다.
그런 다음 state를 초기 상태로 설정하는 대신 초기 state
를 반환하는 함수로 만들어야 합니다. 이는 구성 요소의 data
옵션과 유사합니다. Vuex 4에서 했던 방식과 매우 유사하게 getters
를 작성하지만 각 getter에 대한 매개변수로 state
를 사용하는 대신 this
사용하여 상태에 도달할 수 있습니다. 같은 방식으로 actions
은 전달되는 context
개체에 대해 걱정할 필요가 없습니다. 모든 것에 액세스하기 위해 this
사용할 수 있습니다. 마지막으로 mutations
가 없습니다. 대신, 돌연변이는 actions
과 결합됩니다. 기아는 돌연변이가 단순 setter가 되어 무의미하게 장황하게 만들어 제거하는 경우가 너무 많다고 지적했습니다. 그는 상점 외부에서 직접 상태를 변경하는 것이 "괜찮은지" 여부에 대해 언급하지 않았지만, 우리는 액션에서 직접 상태를 변경하는 것이 확실히 허용되고 권장됩니다. Flux 패턴은 상태의 직접적인 돌연변이에 눈살을 찌푸립니다.
참고 : 구성 요소 생성을 위한 옵션 API보다 구성 API를 선호하는 사용자를 위해 구성 API를 사용하는 것과 유사한 방식으로 저장소를 생성하는 방법도 있음을 알게 되어 기쁩니다.
import { ref, computed } from 'vue' import { defineStore } from 'vuex' export const counterStore = defineStore('counter', { const count = ref(0) const double = computed(() => count.value * 2) function increment () { count.value++ } return { count, double, increment } })
위에 표시된 대로 이름은 defineStore
의 첫 번째 인수로 전달됩니다. 나머지는 컴포넌트의 합성 함수처럼 보입니다. 이것은 옵션 API를 사용한 이전 예제와 정확히 동일한 결과를 산출합니다.
스토어 인스턴스화하기
Vuex 4에서는 Vuex 3에서 상황이 변경되었지만 손에 닿지 않도록 v4만 살펴보겠습니다. v4에서 createStore
를 호출할 때 이미 인스턴스화했습니다. 그런 다음 app.use
를 통해 또는 직접 앱에서 사용할 수 있습니다.
import { createApp } from 'vue' import App from './App.vue' // Your root component import store from './store' // The store definition from earlier const app = createApp(App) app.use(store) app.mount('#app') // Now all your components can access it via `this.$store` // Or you can use in composition components with `useStore()` // ----------------------------------------------- // Or use directly... this is generally discouraged import store from './store' store.state.count // -> 0 store.commit('increment') store.dispatch('increment') store.getters.double // -> 4
이것은 Vuex 5가 v4에서보다 조금 더 복잡하게 만드는 것 중 하나입니다. 이제 각 앱은 Vuex의 별도 인스턴스를 얻을 수 있으므로 각 앱은 데이터를 공유하지 않고도 동일한 스토어의 개별 인스턴스를 가질 수 있습니다. 앱 간에 스토어 인스턴스를 공유하려는 경우 Vuex 인스턴스를 공유할 수 있습니다.
import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' // Your root component const app = createApp(App) const vuex = createVuex() // create instance of Vuex app.use(vuex) // use the instance app.mount('#app')
이제 모든 구성 요소가 Vuex 인스턴스에 액세스할 수 있습니다. 상점 정의를 직접 제공하는 대신 사용하려는 구성 요소로 가져오고 Vuex 인스턴스를 사용하여 인스턴스화하고 등록합니다.
import { defineComponent } from 'vue' import store from './store' export default defineComponent({ name: 'App', computed: { counter () { return this.$vuex.store(store) } } })
$vuex.store
를 호출하면 Vuex 인스턴스에 저장소를 인스턴스화하고 등록합니다. 그 시점부터 해당 저장소에서 $vuex.store
를 사용할 때마다 다시 인스턴스화하는 대신 이미 인스턴스화된 저장소를 다시 제공합니다. createVuex()
에 의해 생성된 Vuex 인스턴스에서 바로 store
메소드를 호출할 수 있습니다.
이제 this.counter
를 통해 해당 구성 요소에서 상점에 액세스할 수 있습니다. 구성 요소에 구성 API를 사용하는 경우 this.$vuex.store
useStore
를 사용할 수 있습니다.
import { defineComponent } from 'vue' import { useStore } from 'vuex' // import useStore import store from './store' export default defineComponent({ setup () { const counter = useStore(store) return { counter } } })
저장소를 구성 요소로 직접 가져와서 인스턴스화하는 데에는 장단점이 있습니다. 코드 분할을 허용하고 필요한 곳에만 저장소를 느리게 로드하지만 이제는 부모가 주입하는 대신 직접 종속성이 됩니다(사용할 때마다 가져와야 한다는 것은 말할 것도 없습니다). 종속성 주입을 사용하여 앱 전체에 제공하려는 경우, 특히 코드 분할이 도움이 되지 않는 앱의 루트에서 사용된다는 것을 알고 있다면 다음을 사용할 provide
있습니다.
import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' import store from './store' const app = createApp(App) const vuex = createVuex() app.use(vuex) app.provide('store', store) // provide the store to all components app.mount('#app')
그리고 그것을 사용할 구성 요소에 주입할 수 있습니다.
import { defineComponent } from 'vue' export default defineComponent({ name: 'App', inject: ['store'] }) // Or with Composition API import { defineComponent, inject } from 'vue' export default defineComponent({ setup () { const store = inject('store') return { store } } })
나는 이 추가 장황함에 대해 흥분하지 않지만 더 명확하고 유연하며 내가 팬입니다. 이러한 유형의 코드는 일반적으로 프로젝트 시작 시 즉시 한 번 작성되며 다시는 귀찮게 하지 않습니다. 하지만 이제는 각각의 새 저장소를 제공하거나 사용할 때마다 가져와야 하지만 코드 모듈을 가져오거나 삽입하는 것은 일반적으로 다른 작업을 수행하는 방식이므로 사람들이 이미 작업하는 방식에 따라 Vuex가 더 잘 작동하도록 만들 뿐입니다.
스토어 이용하기
구성 API를 사용하는 구성 요소와 동일한 방식으로 저장소를 정의하는 유연성과 새로운 방법에 대한 팬이 되는 것 외에도 다른 모든 것보다 나를 더 흥분하게 만드는 것이 한 가지 더 있습니다. 바로 저장소가 사용되는 방식입니다. Vuex 4에서 스토어를 사용하는 모습입니다.
store.state.count // Access State store.getters.double // Access Getters store.commit('increment') // Mutate State store.dispatch('increment') // Run Actions
State
, getters
, mutations
및 actions
은 모두 다른 속성이나 메서드를 통해 다른 방식으로 처리됩니다. 이것은 내가 앞에서 칭찬한 명시성의 이점이 있지만 이 명시성은 실제로 우리에게 아무 것도 얻지 못합니다. 그리고 이 API는 네임스페이스 모듈을 사용할 때만 사용하기가 더 어려워집니다. 이에 비해 Vuex 5는 일반적으로 원하는 대로 정확하게 작동하는 것으로 보입니다.
store.count // Access State store.double // Access Getters (transparent) store.increment() // Run actions // No Mutators
상태, 게터 및 작업 등 모든 항목을 저장소 루트에서 직접 사용할 수 있으므로 훨씬 적은 정보로 사용이 간편하고 옵션 API 또는 작성을 위해 mapState
, mapGetters
, mapActions
및 mapMutations
를 사용할 필요가 거의 없습니다. 합성 API를 위한 추가 computed
된 명령문 또는 간단한 함수. 이렇게 하면 Vuex 스토어가 직접 구축하는 일반 스토어처럼 보이고 작동하지만 플러그인, 디버깅 도구, 공식 문서 등의 모든 이점을 얻을 수 있습니다.
작곡가게
오늘 살펴볼 Vuex 5의 마지막 측면은 구성 가능성입니다. Vuex 5에는 단일 저장소에서 모두 액세스할 수 있는 네임스페이스 모듈이 없습니다. 이러한 각 모듈은 완전히 별도의 저장소로 분할됩니다. 구성 요소를 처리하기에 충분히 간단합니다. 필요한 저장소를 가져와서 실행하고 사용하기만 하면 됩니다. 그러나 한 상점이 다른 상점과 상호 작용하기를 원하면 어떻게 될까요? v4에서는 네임스페이스가 전체를 복잡하게 만들기 때문에 commit
및 dispatch
호출에서 네임스페이스를 사용하고 rootGetters
및 rootState
를 사용한 다음 getter 및 상태에 액세스하려는 네임스페이스까지 작업해야 합니다. Vuex 5에서 작동하는 방법은 다음과 같습니다.
// store/greeter.js import { defineStore } from 'vuex' export default defineStore({ name: 'greeter', state () { return { greeting: 'Hello' } } }) // store/counter.js import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore({ name: 'counter', // Then `use` the store use () { return { greeter: greeterStore } }, state () { return { count: 0 } }, getters: { greetingCount () { return `${this.greeter.greeting} ${this.count}' // access it from this.greeter } } })
v5에서는 사용하려는 저장소를 가져온 다음 use
등록을 하면 이제 지정한 속성 이름으로 저장소 전체에서 액세스할 수 있습니다. 스토어 정의의 구성 API 변형을 사용하는 경우 상황이 훨씬 더 간단합니다.
// store/counter.js import { ref, computed } from 'vue' import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore('counter', ({use}) => { // `use` is passed in to function const greeter = use(greeterStore) // use `use` and now you have full access const count = 0 const greetingCount = computed(() => { return `${greeter.greeting} ${this.count}` // access it like any other variable }) return { count, greetingCount } })
더 이상 네임스페이스 모듈이 없습니다. 각 매장은 분리되어 별도로 사용됩니다. use
하여 다른 스토어 내에서 스토어를 구성할 수 있도록 할 수 있습니다. 두 예에서 use
은 기본적으로 이전의 vuex.store
와 동일한 메커니즘이며 올바른 Vuex 인스턴스로 상점을 인스턴스화하도록 합니다.
TypeScript 지원
TypeScript 사용자의 경우 Vuex 5의 가장 큰 측면 중 하나는 단순화로 인해 모든 것에 유형을 더 간단하게 추가할 수 있다는 것입니다. Vuex의 이전 버전으로 인해 거의 불가능했던 추상화 계층은 Vuex 4를 통해 유형 사용 능력을 향상시켰지만 적절한 유형 지원을 얻기에는 여전히 수동 작업이 너무 많았습니다. 반면 v5에서는 , 원하는 대로 유형을 인라인으로 넣을 수 있습니다.
결론
Vuex 5는 내가 그리고 아마도 다른 많은 사람들이 기대했던 것과 거의 정확히 일치하는 것 같으며 충분히 빨리 오지 않을 것이라고 생각합니다. Vuex의 대부분을 단순화하여 관련된 정신적 오버헤드를 제거하고 유연성을 추가하는 경우에만 더 복잡하거나 장황해집니다. 이러한 변경 사항에 대한 귀하의 생각과 그 대신 또는 추가로 수행할 수 있는 변경 사항에 대해 아래에 의견을 남겨주세요. 또는 소스로 바로 이동하여 RFC(Request for Comments)를 목록에 추가하여 핵심 팀의 생각을 확인하십시오.