Vuex4에서 모델을 만들기 위해 vuex-typing을 만들었습니다.
45106 단어 TypeScriptvuejsVuextech
최근 글로벌 상태를 안전하게 다룰 수 있는 피니아가 등장한 만큼 새로 사용할 때는 일단 논의하는 게 좋다.한편, Vuex에서 이전하는 것은 상당히 어렵기 때문에 기존의 Vuex 자원을 모델로 삼으려면 이 글에서 소개한 vuex-typing과 다른 선택 항목도 선택 항목이 될 수 있다.
업무 중에 Vuex를 사용할 기회가 있는 것 같아서 계속 공부하고 있습니다. 그런데 모델에 대한 지원이 충분하지 않고 불만이 있어서 고정된 조수 라이브러리를 만들었습니다.
Vuex의 유형 정의가 부족합니다.
어플리케이션이 조만간 커질 수 있음을 감안하여 처음부터 모듈로 나누어 관리하는 것이 좋으나 Vuex4에서는 모듈의 모델 지원이 거의 없다
공식 양식
export const store = createStore({
// ...
modules: {
counter: {
namespaced: true,
state: () => {
return {
count: 0
}
},
mutations: {
increment: (state /* any になる */) => {
state.count = state.count + 1
}
},
actions: {
INCREMENT: (context /* any になる */) => {
context.commit('increment')
}
}
}
},
})
이 문제를 해결하기 위해서는 다음과 같은 모듈의 유형을 개별적으로 정의해야 합니다.자체 설계
type CounterState = {
count: number
}
type CounterMutation = {
increment: (state: CounterState) => void
}
type CounterContext = {
commit: <Key extends keyof CounterMutation>( // 試してないので間違ってるかもだけど、概ねこういうの
key: Key,
payload?: CounterMutation<typeof key> extends (state: any, payload: infer I) => any
? I
: never
) => ReturnType<CounterMutation<typeof key>>,
/* getters, dispatch も必要 */
}
type CounterAction = {
INCREMENT: (context: CounterContext) => Promise<void>
}
export const counterModule = {
state: (): CounterState => {
return {
count: 0
}
},
mutations: {
increment: (state: CounterState) => {
state.count = state.count + 1
}
},
actions: {
INCREMENT: ({ commit }: CounterContext) => {
commit('increment')
}
}
}
이렇게 되면 기본적으로 고정된 상태에서 모듈을 쓸 수 있지만 선언문과 실복을 분리해야 하기 때문에 지루해지고 차이가 발생할 때도 유형 정의와 실복을 양방면으로 수정해야 하기 때문에 번거롭다모듈뿐만 아니라 정의된 액션스나 Getters를 호출할 때도 모델을 제공하지 않습니다
const store = useStore()
store.dispatch('INCREMENT') /* 引数にも戻り値にも型付けされない */
저는 이곳의 모델을 가장 원하기 때문에 이 근처의 모델을 만들 방법을 생각해서 vuex-typing을 만들었습니다.가져오기
npm에서 추가vuex@4와vuex-typing
yarn add vue@next vuex@next vuex-typing
모듈 유형
vuex-typing에서 모델에 사용되는 함수를 통해 실제 장치에서 유형 정보를 선택하여 모델을 진행합니다
위의 예는 vuex-typing에서 다음과 같이 쓸 수 있다
store/modules/counter.ts
import { defineModule } from 'vuex-typing'
export const counterModuleName = "counter"
const counterModule = defineModule({
state: () => ({
count: 0
}),
mutations: {
increment: (state /* 明示せずとも state の型が付く */) => {
state.count = state.count + 1
}
}
}, {
// actions
INCREMENT: ({ commit } /* 明示せずとも context の型が付く */) => {
commit('increment_typo') // 型エラー (登録していないミューテーション)
commit("increment", 20) // 型エラー (payload の型が異なる)
commit('increment') // OK
}
})
type CounterModule = typeof counterModule
성형된 거 알아요.액션스의mutation 모델만 수집하기 위해 두 번째 파라미터로 드릴게요.
defineModule에서 actions는 첫 번째 파라미터와 결합하여
namespaced: true
를 지정하여 모듈 옵션을 만듭니다.전송된 defineModule
export function defineModule(module, actions) {
return {
...module,
namespaced: true,
actions: actions,
};
}
따라서 반환 값을 Vuex Store에 직접 전달함으로써 모듈을 등록할 수 있습니다.※ 모델이 복잡해져 강제 진행
namespaced: true
store 모델
Vuex4의use Store에서store는 모델이 있지만 처음에 쓴 것처럼
getters
와dispatch
에 모델을 추가할 수 없기 때문에 vuex-typingTypedStore
로 유형 정의를 생성하고 정식적으로TypeScript Support | Vuex
덮어쓰기 형식 정의useStore 정의
store/index.ts
import { createStore } from "vuex"
import { TypedStore } from "vuex-typing"
import { counterModuleName, counterModule } from "./modules/counter"
export type RootState = {}
export type ModuleType = {
[counterModuleName]: typeof counterModule
}
export type RootStore = TypedStore<RootState, ModuleType>
export const store = createStore<RootState>({
state: {
rootVal: "ok",
},
modules: {
[counterModuleName]: counterModule,
},
})
store/util.tsimport { InjectionKey } from "vue"
import { useStore as baseUseStore } from "vuex"
import type { RootStore } from "."
export const key: InjectionKey<RootStore> = Symbol()
export function useStore(): RootStore {
return baseUseStore(key)
}
이렇게 하면 독자적으로 정의된useStore
포맷된 상태dispatch
또는 모듈의 상태를 사용할 수 있다sample-component.ts
import { useStore } from '~/store/util'
const store = useStore()
store.state.counter.count // :number
const result /* :Promise<void> */ = store.dispatch('counter/INCREMENT') // OK
store.dispatch('counter/INCREMENT_TYPO') // 型エラー
store.dispatch('counter/INCREMENT', 20) // payload の型エラー
또한 TypedStore
에 정의된 유형을 사용하여 Option API를 포맷할 수 있습니다.참조: TypeScript Support | Vuex#typing-store-property-in-vue-component
@types/vuex.d.ts
import type { RootStore } from "../src/store/index"
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
$store: RootStore
}
}
이렇게 해서 this.$store
도 모델이 생겼어요.mapHelpers 사용
맵Helpers(maptate,mapGetters,mapActions)에서도 모델을 진행할 수 있습니다.
useStore
마찬가지로 vuex-typing의 형식으로 덮어쓰기 형식을 정의합니다.store/util.ts
import {
mapGetters as baseMapGetters,
mapState as baseMapState,
mapActions as baseMapActions,
} from "vuex"
import { MapState, MapGetters, MapActions } from "vuex-typing"
import type { RootStore, ModuleType } from "."
export const mapState = baseMapState as unknown as MapState<
RootStore["state"],
ModuleType
>
export const mapGetters = baseMapGetters as unknown as MapGetters<ModuleType>
export const mapActions = baseMapActions as unknown as MapActions<ModuleType>
구성 요소는 덮어쓰기 형식 정의의 맵Helpers 함수를 사용합니다sample-component.ts
import { defineComponent } from "vue"
import { mapState, mapGetters, mapActions } from "../store/util"
defineComponent({
computed: {
...mapState("counter", {
count: (state /* 型付けされてる */) => state.count,
}),
// モジュール名、プロパティ('cnt', 'PLUS_N' 等)が間違っていたら型エラーに
...mapGetters("counter", ["cnt"]),
...mapGetters(["counter/cnt"]),
},
methods: {
...mapActions(["counter/INCREMENT"]),
...mapActions("counter", ["PLUS_N"]),
test() {
// mapState
this.count // :number
// mapGetters
this["counter/cnt"] // :number
this.cnt // :number
// mapActions
this["counter/INCREMENT"]()
this.PLUS_N(20)
},
},
})
자신의 Getters와 actions를 참조하세요.
Vuex의 모듈에서는 모듈에 발표된 다른 getters, actions를 사용할 수 있지만, 순환 인용이기 때문에 이 유형을 진행할 수 없습니다.명확한 형식으로 대체할 수 있다
import { defineModule, LocalGetters, LocalDispatch } from "vuex-typing"
export const counterModule = defineModule({
getters: {
cnt: (state) => state.count,
cnt2: (_state, _getters): number /* 循環参照になるので明示する必要がある */ => {
const getters = _getters as LocalGetters<CounterModule["getters"]> // 上書き
return getters.cnt
},
},
{
INCREMENT: ({ commit }): void => {
commit("increment")
},
PLUS_N_LOOP: ({ dispatch: _dispatch }, n: number) => {
const dispatch: LocalDispatch<CounterModule["actions"]> = _dispatch // 上書き
for (const _i of new Array(n)) {
dispatch("INCREMENT")
}
},
}
})
type CounterModule = typeof counterModule
이상이 주요한 사용 방법이다.모든 샘플이 정식 창고의 example에 있습니다.
yarn create @vitejs/app example --template vue-ts
에 생성된 보일러판에 vuex-typing의 예를 놓아 복제 후 직접 테스트할 수 있다.할 수 없는 일
Vuex의 사용 방법은 매우 유연하다. 한마디로 전역 상태를 관리하는 것이고 여러 가지 상태 관리, 호출 방법도 있다. 모두 포맷할 수 없기 때문에 기능을 삭감하고 있다.
someGetter(state, getters, rootState, rootGetters)
에서는 getters, 루트 State, 루트 Getters에 접근할 수 있으나 이 모델을 진행할 수 없습니다(루트 State와 getters만 형식 정의를 명시하고 덮어쓸 수 있습니다)비
nampespaced: true
모듈vuex-typing
를 사용할 때 모든 상태를 모듈식으로 관리하는 것이 좋다(전 세계에서 상태나 동작 등이 일어나지 않는다).(일관성이 있는 독자는 읽을 때도 쉽게 읽을 수 있다)기타 유형 옵션
프로그램 라이브러리를 만들기 전에 여러 가지 종류의 옵션을 찾아보았기 때문에 총괄해 보겠습니다.
Vuex5
Vuex5의 경우 API가 많이 바뀔 것 같지만 TypeScript의 fulsa 포트가 들어가기 때문에 앞으로 Vuex5가 출시되면 새로운 Vuex를 추가하면 차분하게 Vuex5의 스타일링을 하는 것이 좋습니다.
하지만 API가 많이 변했기 때문에 기존 Vuex에서 시작하는 마이그레이션은 어려울 것 같습니다.
참조: Vuex5로 어떻게 변해요?
ktsn/vuex-type-helper
처음에 쓴 Context 등 형식 정의를 준비한 ktsn/vuex-type-helper 형식의 조수 라이브러리가 있습니다.
필기 정의와 실복을 분리해야 한다는 점은 변하지 않지만, 콘텍스트 등 장편 장편 장르의 조수로서 준비돼 있기 때문에 자신보다 준비하는 것이 좋다.
이 글에서 사용한 예
vuex-type-helper
는 다음과 같다.견본
import * as Vuex from 'vuex'
import { DefineMutations, DefineActions, Dispatcher, Committer } from 'vuex-type-helper'
// 型定義
export interface CounterState {
count: number
}
export interface CounterMutation {
increment: void
}
export interface CounterActions {
INCREMENT: void
}
// 実装
const state: CounterState = {
count: 0
}
const mutations: DefineMutations<CounterMutations, CounterState> = {
increment (state) {
state.count = statel.count + 1
}
}
const actions: DefineActions<CounterActions, CounterState, CounterMutations, CounterGetters> = {
INCREMENT ({ commit }) {
commit('increment')
}
}
paroi-tech/direct-vuex
paroi-tech/direct-vuex라는 프로그램 라이브러리가 있는데, 여기서도
vuex-typing
와 같은 방법으로 모델을 진행하였다.느낌은 좋지만 Vuex 기준 이외의 동작과 트위터 호출, 점포 로그인 방법도 좀 달라요. 허락해주셨으면 좋겠어요.
견본
store.dispatch("mod1/myAction", myPayload) // Vuex 標準
store.dispatch.mod1.myAction(myPayload) // direct-vuex での書き方
나는 이미 있는 것 같아서 찾아보니 이 프로그램 라이브러리를 발견하였다스타일 지침은 좋지만 조만간 Type Script의 지원이 있을 것을 고려해 기준을 벗어나고 싶지 않아 했다vuex-typing
(Vuex5에서 API가 바뀌었다고 하지만 기준에 따라 하면 이전 원가가 비교적 낮을 것)끝맺다
이상vuex-typing의 소개입니다.
새로운 Vuex를 사용할 기회가 있다면 꼭 사용해 주세요.
참고 자료
Reference
이 문제에 관하여(Vuex4에서 모델을 만들기 위해 vuex-typing을 만들었습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/kimuson/articles/vuex_typing_텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)