Vue 进阶系列丨Vuex+TS 代码提示

184fa0e08391cb9cad186f5527c577c0.png

Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!


2013年7月28日,尤雨溪第一次在 GItHub 上为 Vue.js 提交代码;2015年10月26日,Vue.js 1.0.0版本发布;2016年10月1日,Vue.js 2.0发布。

最早的 Vue.js 只做视图层,没有路由, 没有状态管理,也没有官方的构建工具,只有一个库,放到网页里就可以直接用了。

后来,Vue.js 慢慢开始加入了一些官方的辅助工具,比如路由(Router)、状态管理方案(Vuex)和构建工具(Vue-cli)等。此时,Vue.js 的定位是:The Progressive Framework。翻译成中文,就是渐进式框架。

Vue.js2.0 引入了很多特性,比如虚拟 DOM,支持 JSX 和 TypeScript,支持流式服务端渲染,提供了跨平台的能力等。Vue.js 在国内的用户有阿里巴巴、百度、腾讯、新浪、网易、滴滴出行、360、美团等等。

Vue 已是一名前端工程师必备的技能,现在就让我们开始深入学习 Vue.js 内部的核心技术原理吧!


什么是vuex

vuex 是 vue 中用来做状态管理的一个仓库,一般较大型的项目才有必要使用它,因为如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。它主要包括state、mutations、actions、getters、modules 五个核心概念,有关具体的使用,我们这篇文章不做重点讲解,这里主要告诉大家如何在vuex中集成 typescript,做到代码提示功能。


最终效果预览

我们先看一下最终要达到的效果是什么样的:

1. 拿到 getters: 自动提示每个模块中定义的 getters,不用再回到定义的地方确认每一个 getters 的命名是否正确了

5e9a5bc289ef23b0c618aaadbccc2fc3.png

2. 调用 mutation:

d944cd752ea24fe2faf5036bf0dab4c4.png

3. 调用 actions

631f0315190c45e9dfd7a973c11dcf90.png

最终的效果就是,在我们输入引号的时候,编辑器会自动弹出提示,我们可以快速找到目标方法或者 getters 了,减少了代码出错的可能性。


实现步骤

1. 创建项目,选用 vue-ts 开发模版

npm init vite@latest

2. 引用 vuex

npm install vuex@next --save

3. 在 src 下新建 store 目录,然后 store 下新建 index.ts

// store.ts
import { InjectionKey } from 'vue'
import { createStore, useStore as baseUseStore, Store } from 'vuex'
// 引入tab模块
import tabs, { TabsState } from './modules/tabs'
// 引入menu模块
import menu, { MenuState } from './modules/menu'
// 引入vuex-ts自定义类型推断类,代码提示的核心
import { CommonStore } from './vuex_ts'


// 定义根级State类型
export type RootState = {
    tabs: TabsState,
    menu: MenuState
}


// 把多个模块聚合在一起
export const modules = {
    tabs: tabs,
    menu: menu
}


export const key: InjectionKey<Store<RootState>> = Symbol()
export const store = createStore<RootState>({
    modules
}) as CommonStore


// 定义自己的 `useStore` 组合式函数
export function useStore(): CommonStore {
    return baseUseStore(key);
}

4. 在 store 文件夹下,新建 modules 文件夹,并新建两个 ts 文件,这里每个ts文件相当于一个 vuex 模块,分别是:tabs.ts、menu.ts

// menu.ts


import {RootState} from '@/store'
import { ActionContext } from 'vuex';


// 定义menu模块下的,state的类型
export type MenuState = {
    count:number;
}


// 定义menu模块下的state
export const state: MenuState = {
    count:0
}
// 定义menu模块下的mutations
export const mutations = {
    setCount(state:MenuState, count:number):void{
        state.count = count
    }
}
// 定义menu模块下的actions
export const actions = {
    setAsyncCount({commit}:ActionContext<MenuState,RootState>){
        setTimeout(() => {
            state.count++
        }, 1000);
    }
}
// 定义menu模块下的getters
export const getters = {
    getCount(state:MenuState){
        return state.count
    }
}


export default {
    namespaced:true, // 声明命名空间
    state,
    mutations,
    actions,
    getters
}
// tabs.ts


import { ActionContext } from "vuex"
import { RootState } from "@/store"


// 定义单个tab的类型
interface ITab{
    title:string,
    path:string
}
// 定义tabs模块下的,state的类型
export type TabsState = {
    tabLists: Array<ITab>
}
// 定义tabs模块下的state
export const state: TabsState = {
    tabLists:[],
}
// 定义tabs模块下的mutations
export const mutations = {
    addTab(state:TabsState, tab: ITab):void{
        if(state.tabLists.some(item => item.path === tab.path)){return}
        state.tabLists.push(tab);
    }
}
// 定义tabs模块下的actions
export const actions = {
    addAsyncTabs({commit}:ActionContext<TabsState,RootState>,tab:ITab){
        if(state.tabLists.some(item => item.path === tab.path)){return}
        state.tabLists.push(tab);
    }
}
// 定义tabs模块下的getters
export const getters = {
    getTabs(state:TabsState){
        return state.tabLists
    }
}


export default {
    namespaced:true, // 声明命名空间
    state,
    mutations,
    actions,
    getters
}

5. 在store文件夹下,新建vuex_ts.ts文件

// vuex_ts.ts


import {modules,RootState} from './index'
import { CommitOptions, DispatchOptions, Store as VuexStore } from 'vuex'
// 获取modules的类型
type Modules = typeof modules
// 获取所有模块下的mutations
type GetMutation<T> = T extends {mutations:infer G} ? G : never;
type GetMutations<T> = {
    [K in keyof T]:GetMutation<T[K]>
}
type mutationsObj = GetMutations<Modules>


// 获取所有模块下的actions
type GetAction<T> = T extends {actions:infer G} ? G : never;
type GetActions<T> = {
    [K in keyof T]:GetAction<T[K]>
}
type actionsObj = GetActions<Modules>


// 获取所有模块下的getters
type GetGetter<T> = T extends {getters:infer G} ? G : never;
type GetGetters<T> = {
    [K in keyof T]:GetGetter<T[K]>
}
type getterObj = GetGetters<Modules>


// tabs/addTabs  menu/setCount
type AddPrefix<prefix,keys> = `${prefix & string}/${keys & string}` 
type GetKey<T,K> = AddPrefix<K,keyof T>;
type Getkeys<T> = {
    [K in keyof T]:GetKey<T[K],K>
}[keyof T];
type ss = Getkeys<mutationsObj>


// 获取当前模块下每个函数的返回值
type GetFunc<T,A,B> = T[A & keyof T][B & keyof T[A & keyof T]]
type GetMethod<T> = {
    [K in Getkeys<T>]:K extends `${infer A}/${infer B}` ? GetFunc<T,A,B> : unknown
}


type GetMutationsFunc = GetMethod<mutationsObj>
type GetActionsFunc = GetMethod<actionsObj>
type GetGettersFunc = GetMethod<getterObj>


// 去掉之前的,使用自己定义的
export type CommonStore = Omit<VuexStore<RootState>, 'commit' | 'getters' | 'dispatch'> & {
    commit<K extends keyof GetMutationsFunc, P extends Parameters<GetMutationsFunc[K]>[1]>(
        key: K,
        payload?: P,
        options?: CommitOptions
    ): ReturnType<GetMutationsFunc[K]>
} & {
    getters: {
        [K in keyof GetGettersFunc]: ReturnType<GetGettersFunc[K]>
    }
} & {
    dispatch<K extends keyof GetActionsFunc>(
        key: K,
        payload?: Parameters<GetActionsFunc[K]>[1],
        options?: DispatchOptions
    ): ReturnType<GetActionsFunc[K]>
}

6. 代码目录:

这里给大家展示一下store文件夹下的代码目录,以便大家理解

d5e8cc1cdc97e245d68c2f3af4b72424.png


效果查看

到这里,我们的vuex集成ts,生成代码提示就已经实现了,我们这里看下效果:

<template>
</template>


<script setup lang='ts'>
// 这里要注意,引入的是我们自定义的useStore,不是vuex里面的useStore
import { useStore } from './store';
const store = useStore()
store.getters['menu/getCount']
store.dispatch('menu/setAsyncCount')
store.commit('menu/setCount')
</script>

cd1feec732f070e3e5a41741c0c80ba2.png


Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!

b1bc3690d71529d9fb4ab5f8744cea45.png

叶阳辉

HFun 前端攻城狮

往期精彩:

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
VueVuex和TypeScript中使用modules进行模块化的方式如下: 首先,我们需要安装好VueVuex,并使用TypeScript构建Vue项目。可以通过以下命令进行安装: ``` npm install vue vuex --save npm install typescript ts-loader --save-dev ``` 接下来,在根目录下创建`store`目录,并在该目录下创建`index.ts`文件。在`store`目录下再创建`modules`目录,并在该目录下创建对应的模块文件,例如`user.ts`和`product.ts`。 在`user.ts`模块中,定义用户相关的状态、mutations、actions和getters。例如: ```javascript // user.ts const user = { state: { userInfo: null }, mutations: { SET_USER_INFO(state, userInfo) { state.userInfo = userInfo; } }, actions: { setUserInfo({ commit }, userInfo) { commit('SET_USER_INFO', userInfo); } }, getters: { getUserInfo: state => state.userInfo } }; export default user; ``` 在`product.ts`模块中,同样定义商品相关的状态、mutations、actions和getters。 在`index.ts`文件中,引入VueVuex,并创建一个Vuex Store对象,使用`module`方法将模块添加到Store中。例如: ```javascript // index.ts import Vue from 'vue'; import Vuex from 'vuex'; import user from './modules/user'; import product from './modules/product'; Vue.use(Vuex); const store = new Vuex.Store({ modules: { user, product } }); export default store; ``` 最后,在Vue的入口文件(如`main.ts`)中,引入创建的Vuex Store,并将其挂载到Vue实例上。例如: ```javascript // main.ts import Vue from 'vue'; import App from './App.vue'; import store from './store/index'; Vue.config.productionTip = false; new Vue({ store, render: h => h(App) }).$mount('#app'); ``` 现在,我们就可以在组件中使用`this.$store`来访问Vuex的状态、提交mutations、触发actions等。在模块化的情况下,可以使用`this.$store.state.user.userInfo`来访问`user`模块中的`userInfo`状态,使用`this.$store.commit('user/SET_USER_INFO', userInfo)`来提交`user`模块中的`SET_USER_INFO`mutation,使用`this.$store.dispatch('user/setUserInfo', userInfo)`来触发`user`模块中的`setUserInfo`action,以此类推。 通过以上的步骤,我们就可以在VueVuex和TypeScript中使用modules进行模块化管理应用的状态。这样可以使得代码结构更清晰,易于维护和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值