阅读 887

Vue3 TypeScript 配置 Vuex4

本文记录 Ts 版的 Vue3 项目配置 Vuex

目录 ./src/store

├─ store
│  ├─ index.ts
│  └─ interface.ts
复制代码

首先看 ./src/store/index.ts ,这里是整个 store 的入口,里面定义并默认导出了 createStore 方法创建的 store 供整个项目使用。

import { InjectionKey } from 'vue';
import { createStore, Store } from 'vuex';

import RootStateTypes from '@/store/interface';

export default createStore<RootStateTypes>({
  state: {
    test:'test'
  },
  getters :{
  },
  mutations: {
    CHANGE_TEST(state,val){
      state.test = val;
    }
  },
  actions: {
  },
  modules: {
    testModule
  }
});
export const key: InjectionKey<Store<RootStateTypes>> = Symbol('vue-store');
复制代码
  • InjectionKey 方法为创建并使用定义 state 时提供唯一的 key,用到了 ES6Symbol

./src/store/interface.ts ,定义并导出了所用状态的接口。

export default interface RootStateTypes{
  test: string;
}
复制代码

./src/main.ts 中引入 store

import { createApp } from 'vue'
import store, { key } from './store'
...
createApp(App as any)
  .use(store,key)
  ...
复制代码
  • 注意引入方式,这里引入了之前的唯一值 key,并在 createAppuse 方法中和 store 一起传入

测试

<template>
  <div>
    <p>{{moduleTest}}</p>
    <button @click="handlerChnage">测试</button>
  </div>
</template>
<script lang="ts">
import { computed } from 'vue';
import { useStore } from 'vuex';
import { key } from '@/store'
export default {
  name: 'Test',
  setup(){
    const store = useStore(key);
    let moduleTest = computed(()=>store.state.test);
    const handlerChnage = () => {
      store.commit('CHANGE_TEST','test2021');
    }
    return {
      moduleTest,
      handlerChnage
    }
  }
};
</script>
复制代码
  • 这里的 import { useStore } from 'vuex';

import { key } from '@/store'; 两条语句分别引入 useStorekey

  • 调用时 const store = useStore(key); 也要传入 key

每次页面中使用时都要引入并传入 key 有些繁琐,我们可以在 ./src/store/index.ts 中统一导出。

import { InjectionKey } from 'vue';
import { createStore, Store, useStore as baseUseStore } from 'vuex';

import RootStateTypes from '@/store/interface';

export default createStore<RootStateTypes>({
  state: {
    test:'test'
  },
  getters :{
  },
  mutations: {
    CHANGE_TEST(state,val){
      state.test = val;
    }
  },
  actions: {
  },
  modules: {
    testModule
  }
});
export const key: InjectionKey<Store<RootStateTypes>> = Symbol('vue-store');
export function useStore(){
  return baseUseStore(key);
}
复制代码

有两处改变:

  1. useStore as baseUseStore 通过 TS 的 as 语法从 vuex 中引入 useStore
  2. 最后导出函数 useStore,相当于包装一下 vuex 中的 useStore 方法,传入 key

优化后再测试,代码会有所简化。

import { useStore } from '@/store';
export default {
  ...
  setup(){
    const store = useStore();
    ...
  }
  ...
}
复制代码
  • 不需要引入 key,也不需要把 key 当作参数传入 useStore方法。

配置子模块

需要修改一下目录 ./src/store

├─ store
│  ├─ modules
│  │  ├─ test
│  │  │  ├─ index.ts
│  │  │  └─ interface.ts
│  ├─ index.ts
│  └─ interface.ts
复制代码
  • 创建 ./src/store/modules/ 文件夹。

  • 为了测试现在此目录创建 test 文件夹。

  • test 相当于一个模块,在这个目录中再创建 index.ts (state) 和 interface (接口)

./src/store/modules/test/index.ts

import { Module } from 'vuex';
import RootStateTypes from '@/store/interface';
import TestModuleTypes from '@/store/modules/test/interface';

const testModule: Module<TestModuleTypes,RootStateTypes> = {
  namespaced: process.env.NODE_ENV !== 'production',
  state: {
    name: 'testmodule',
    count: 0
  },
  mutations: {
    ADD_COUNT(state){
      state.count += 1;
    }
  },
  actions: {
  },
  modules: {
  }
}
export default testModule;
复制代码
  • 整体思路和 ./src/store/index.ts 差不多。只是从 vuex 中引入了 Module 作为输出类型。
  • namespaced 命名空间的使用可提高模块的封装性和可复用性。

./src/store/modules/test/interface.ts

export default interface TestModuleTypes{
  name: string;
  count: number;
}
复制代码
  • 定义子模块的类型。

根模块两个文件的修改:

先看 ./src/store/interface.ts

import TestModuleTypes from '@/store/modules/test/interface'
export default interface RootStateTypes{
  test: string;
}
export interface AllStateTypes extends RootStateTypes{
  testModule: TestModuleTypes;
}
复制代码
  • 引入了刚定义的子模块类型 TestModuleTypes
  • 通过接口继承的方式定义AllStateTypes 接口,里面包含全部子模块接口。

再修改 ./src/store/index.ts

import { InjectionKey } from 'vue';
import { createStore, Store, useStore as baseUseStore } from 'vuex';

import RootStateTypes, { AllStateTypes } from '@/store/interface';

// 引入子模块
import testModule from '@/store/modules/test';

export default createStore<RootStateTypes>({
  state: {
    test:'test'
  },
  getters :{
  },
  mutations: {
    CHANGE_TEST(state,val){
      state.test = val;
    }
  },
  actions: {
  },
  modules: {
    testModule
  }
});

export const key: InjectionKey<Store<RootStateTypes>> = Symbol('vue-store');
export function useStore<T=AllStateTypes>(){
  return baseUseStore<T>(key);
}
复制代码
  • 引入刚定义的 AllStateTypes 子模块接口。
  • 导入对应的子模块 testModule,并把它加入到默认导出的 createStoremodules
  • 最后导出的 useStore 方法添加默认是 AllStateTypes 的泛型

测试子模块

<template>
  <div>
    <p>{{testModuleName}}</p>
    <p>{{testModuleCount}}</p>
    <button @click="handlerCount">添加</button>
  </div>
</template>
<script lang="ts">
import { computed } from 'vue';
import { useStore } from '@/store';
export default {
  name: 'Test',
  setup(){
    const store = useStore();
    const testModuleName = computed(()=>store.state.testModule.name);
    const testModuleCount = computed(()=>store.state.testModule.count);
    const handlerCount = () => {
      store.commit('testModule/ADD_COUNT');
    }
    return {
      testModuleName,
      testModuleCount,
      handlerCount
    }
  }
};
</script>
复制代码
  • 通过 store.state.模块名 的方式引用到子模块。
  • 注意 store.commit 传参的方式 'testModule/ADD_COUNT',是 '模块名/方法名'
文章分类
前端
文章标签