Vuex对Typescript的支持,仍十分薄弱,官方库只是添加了一些.d.ts声明文件,并没有像vue 2.5这样内置支持。

第三方衍生库 vuex-typescriptvuex-ts-decoratorsvuex-typexvuex-class等等,我个人的总结,除了vuex-class外,基本都存在侵入性太强的问题,引用不算友好。而vuex-class提供的功能其实也是薄薄一层,并不能解决核心痛点。因此,需要手动添加辅助的地方,其实颇多。

核心痛点:每次调用 this.$store.dispatch / this.$store.commit / this.$store.statethis.$store.getters 都会伴随着类型丢失。

其中,dispatch/commit 可以通过建立辅助函数形式,简单绕开。 state/getters 没有太好办法,只能手动指定,若觉得麻烦,可以全都指成 any,等官方支持。官方动态见此 issue

动手改造第一步:从 shopping-cart 示例搬运代码

以下示例基于 vuex 官方 examples 中最复杂的一个 shopping-cart
改造后的完整代码见 vue-vuex-typescript-demo

准备工作:

  • shopping-cart代码复制至项目目录下
  • .js文件统一重命名为.ts
  • currency.js/api/shop.js/components/App.vue等外围文件的ts改造
  • npm i -D vuex 添加依赖

动手改造第二步:State改造

用到state变量的地方实在太多,不仅store目录下 action/getter/mutation 均有可能需要,甚至在 .vue 文件里,mapState也有引用,因此我个人总结的一套实践:

  • store/modules下的每个子模块,均维护自己名为 State 的 Interface 声明
  • store/index.ts 文件中,汇总各子模块,维护一个总的State声明

store/modules 下文件举例:

  1. // ./src/store/modules/cart.ts
  2. interface Shape {
  3. id: number
  4. quantity: number
  5. }
  6. export interface State {
  7. added: Shape[]
  8. checkoutStatus: 'successful' | 'failed' | null
  9. }
  10. // initial state
  11. // shape: [{ id, quantity }]
  12. const state: State = {
  13. added: [],
  14. checkoutStatus: null
  15. }
  16. // 需引用state的地方举例:
  17. const getters = {
  18. checkoutStatus: (state: State) => state.checkoutStatus
  19. }

store/index.ts 文件总 State 举例:

  1. // ./src/store/index.ts
  2. import { State as CardState } from './modules/cart'
  3. import { State as ProductsState } from './modules/products'
  4. export interface State {
  5. cart: CardState,
  6. products: ProductsState
  7. }

总 State 引用示例:

  1. // ./src/store/getters.ts
  2. import { State } from './index'
  3. const cartProducts: Getter<State, any> = (state: State) => {
  4. return state.cart.added.map(shape => {
  5. // 此处shape自动推导出Shape类型
  6. // ... 详见源码
  7. })
  8. }

如此,所有直接引用 state 的地方,均可启用类型推导

动手改造之 Mutation

Mutation 对应 store.commit 命令,常见写法:

  1. const mutations = {
  2. [types.ADD_TO_CART] (state, { id }) {
  3. // ...
  4. }
  5. }

state 上步已处理{ id }payload 参数,即为开篇介绍类型缺失的重灾区。

我的一套个人实践:

  • store/modules 下的子模块文件,为自己的mutations 维护 payload Interface声明
  • 子模块共用 payload(多个模块响应同一 commit 等),在 store/index.ts 中统一维护
  • 新建文件 store/dispatches.ts 文件,为每一个直接调用的带参commit维护辅助函数,以应用类型推导

子模块 payload 声明举例:

  1. // ./src/store/modules/products.ts
  2. import { Product, AddToCartPayload } from '../index'
  3. export interface ProductsPayload {
  4. products: Product[]
  5. }
  6. const mutations = {
  7. [types.RECEIVE_PRODUCTS] (state: State, payload: ProductsPayload) {
  8. state.all = payload.products
  9. },
  10. [types.ADD_TO_CART] (state: State, payload: AddToCartPayload) {
  11. const product = state.all.find(p => p.id === payload.id)
  12. // ...
  13. }
  14. }
  15. // mutations调用举例:
  16. const actions = {
  17. getAllProducts (context: ActionContextBasic) {
  18. shop.getProducts((products: Product[]) => {
  19. const payload: ProductsPayload = {
  20. products
  21. }
  22. context.commit(types.RECEIVE_PRODUCTS, payload)
  23. })
  24. }
  25. }

store/index.ts文件公共 payload 声明举例:

  1. // ./src/store/index.ts
  2. export interface AddToCartPayload {
  3. id: number
  4. }

store/dispatches.ts文件,commit辅助函数,参见下步同文件dispatch辅助函数

动手改造之 Action

Action 对应 store.dispatch 命令,常见写法:

  1. const actions = {
  2. checkout ({ commit, state }, products) {
  3. // ...
  4. }
  5. }

其中第二个参数productspayload 参数,用法同上步 Mutation 的 payload 参数,不再赘述。

第一个参数{ commit, state }context参数,vuex 的 d.ts 提供有类型 ActionContext,用法如下:

  1. import { ActionContext } from 'vuex'
  2. const actions = {
  3. checkout (context: ActionContext<State, any>, products: CartProduct[]) {
  4. context.commit(types.CHECKOUT_REQUEST)
  5. // ...
  6. }
  7. }

ActionContext<State, RootState> 传入两个大部分Action根本用不到的参数,才能得到需要的dispatchcommit,在我看来,难用至极。

个人更喜欢如下写法:

  1. const actions = {
  2. checkout (context: { commit: Commit, state: State }, products: CartProduct[]) {
  3. context.commit(types.CHECKOUT_REQUEST)
  4. // ...
  5. }
  6. }

Action payload 改造参见步骤 Mutation,不再赘述。

store/dispatches.ts文件,dispatch辅助函数:

  1. // ./src/store/dispatches.ts
  2. import store, { CartProduct, Product } from './index'
  3. export const dispatchCheckout = (products: CartProduct[]) => {
  4. return store.dispatch('checkout', products)
  5. }

.vue文件调用举例:

  1. // ./src/components/Cart.vue
  2. import { dispatchCheckout } from '../store/dispatches'
  3. export default Vue.extend({
  4. methods: {
  5. checkout (products: CartProduct[]) {
  6. // this.$store.dispatch 写法可用,但不带类型推导
  7. // this.$store.dispatch('checkout', products)
  8. dispatchCheckout(products) // 带有类型智能提示
  9. }
  10. }
  11. })

动手改造之 Getter

Getter常见写法:

  1. const getters = {
  2. checkoutStatus: state => state.checkoutStatus
  3. }

需要改的不多,state 加上声明即可:

  1. const getters = {
  2. checkoutStatus: (state: State) => state.checkoutStatus
  3. }

动手改造之独立的 Mutations/Actions/Getters 文件

独立文件常规写法:

  1. // ./src/store/getters.js
  2. export const cartProducts = state => {
  3. return state.cart.added.map(({ id, quantity }) => {
  4. const product = state.products.all.find(p => p.id === id)
  5. return {
  6. title: product.title,
  7. price: product.price,
  8. quantity
  9. }
  10. })
  11. }

引用:

  1. // ./src/store/index.js
  2. import * as getters from './getters'
  3. export default new Vuex.Store({
  4. getters
  5. })

typescript下均需改造:

  1. // ./src/
  2. import { GetterTree, Getter } from 'vuex'
  3. import { State } from './index'
  4. const cartProducts: Getter<State, any> = (state: State) => {
  5. return state.cart.added.map(shape => {
  6. // ...
  7. })
  8. }
  9. const getterTree: GetterTree<State, any> = {
  10. cartProducts
  11. }
  12. export default getterTree

Actions/Mutations 文件改造同上,类型换成 ActionTree, Action, MutationTree, Mutation即可

引用:

  1. // ./src/store/index.js
  2. import getters from './getters'
  3. export default new Vuex.Store({
  4. getters
  5. })

原因是vuex定义,new Vuex.Store参数类型 StoreOptions 如下:

  1. export interface StoreOptions<S> {
  2. state?: S;
  3. getters?: GetterTree<S, S>;
  4. actions?: ActionTree<S, S>;
  5. mutations?: MutationTree<S>;
  6. modules?: ModuleTree<S>;
  7. plugins?: Plugin<S>[];
  8. strict?: boolean;
  9. }

于是,独立Gettes/Actions/Mutations文件,export 必须是GetterTree/ActionTree/MutationTree类型

动手改造之 .vue 文件调用

  • 传统写法全部兼容,只需 mapState为state添加类型 (state: State) => state.balabal 等很少改动即可正常运行。只是类型均为 any
  • 建议不使用 mapState / mapGetters / mapActions / mapMutations,以明确指定类型
  • dispatch 及 commit 调用可通过上述 store/dispatches.ts 下辅助函数,手动开启类型推导
  • state 及 getters 类型推导,暂时只能手动指定。自动推导,估计得等官方内置支持了。

完整调用示例:

  1. // ./src/components/ProductList.vue
  2. import Vue from 'vue'
  3. // import { mapGetters, mapActions } from 'vuex'
  4. import { Product } from '../store'
  5. import { dispatchAddToCart } from '../store/dispatches'
  6. export default Vue.extend({
  7. computed: {
  8. // ...mapGetters({
  9. // products: 'allProducts'
  10. // })
  11. products (): Product[] {
  12. return this.$store.getters.allProducts
  13. }
  14. },
  15. methods: {
  16. // ...mapActions([
  17. // 'addToCart'
  18. // ])
  19. addToCart (p: Product) {
  20. dispatchAddToCart(p)
  21. }
  22. },
  23. created () {
  24. this.$store.dispatch('getAllProducts')
  25. }
  26. })

vue-class-component + vuex-class 组件式写法

如果觉得以上废弃 mapState / mapGetters 后的写法繁琐,可引入vue-class-component + vuex-class,开启组件式写法

  • vue-class-component,vue官方维护,学习成本低
  • vuex-class,作者 ktsn,vuex及vue-class-component贡献排第二(第一尤雨溪了)的活跃开发者,质量还是有保障的

引入这俩依赖后,须在 tsconfig.json 添加配置:

  1. {
  2. "compilerOptions": {
  3. // 启用 vue-class-component 及 vuex-class 需要开启此选项
  4. "experimentalDecorators": true,
  5. // 启用 vuex-class 需要开启此选项
  6. "strictFunctionTypes": false
  7. }
  8. }

Component 写法示例:

 
  1. import Vue from 'vue'
  2. import { Product } from '../store'
  3. // import { dispatchAddToCart } from '../store/dispatches'
  4. import Component from 'vue-class-component'
  5. import { Getter, Action } from 'vuex-class'
  6.  
  7. @Component
  8. export default class Cart extends Vue {
  9. @Getter('cartProducts') products: CartProduct[]
  10. @Getter('checkoutStatus') checkoutStatus: CheckoutStatus
  11. @Action('checkout') actionCheckout: Function
  12.  
  13. get total (): number {
  14. return this.products.reduce((total, p) => {
  15. return total + p.price * p.quantity
  16. }, )
  17. }
  18.  
  19. checkout (products: CartProduct[]) {
  20. // dispatchCheckout(products)
  21. this.actionCheckout(products)
  22. }
  23. }
  1. 欢迎关注公众号,进一步技术交流:
  1.  

vue + ts Vuex篇的更多相关文章

  1. vue+ts搭建项目

    Tip: 为了避免浪费您的时间,本文符合满足以下条件的同学借鉴参考 1.本文模版不适用于小型项目,两三个页面的也没必要用vue2.对typescript.vue全家桶能够掌握和运用 此次项目模版主要涉 ...

  2. Vuex篇

    [Vuex篇] vuex源码你学会了吗? 我来免费教你!~ 1.vuex是什么?  Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的 ...

  3. Vue学习—— Vuex学习笔记

    组件是Vue最强大的功能之一,而组件实例的作用域是相互独立的,意味着不同组件之间的数据是无法相互使用.组件间如何传递数据就显得至关重要,这篇文章主要是介绍Vuex.尽量以通俗易懂的实例讲述这其中的差别 ...

  4. Vue3: 如何以 Vite 创建,以 Vue Router, Vuex, Ant Design 开始应用

    本文代码: https://github.com/ikuokuo/start-vue3 在线演示: https://ikuokuo.github.io/start-vue3/ Vite 创建 Vue ...

  5. Vue 2.0 + Vue Router + Vuex

    用 Vue.js 2.x 与相配套的 Vue Router.Vuex 搭建了一个最基本的后台管理系统的骨架. 当然先要安装 node.js(包括了 npm).vue-cli 项目结构如图所示: ass ...

  6. Vue之Vuex

    一.什么是vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.简单来说就是一个数据统一 ...

  7. requirejs、vue、vuex、vue-route的结合使用,您认为可行吗?

    在五一节之前和一网友讨论前端技术时,对方提到vue.vue-route如果配合requirejs应用.当时的我没有想得很明白,也没能这位网友一个准确的回复,但我许诺于他五一研究后给他一个回复.本是一天 ...

  8. Vue中Vuex的详解与使用(简洁易懂的入门小实例)

    怎么安装 Vuex 我就不介绍了,官网上有 就是 npm install xxx 之类的.(其实就是懒~~~哈哈) 那么现在就开始正文部分了 众所周知 Vuex 是什么呢?是用来干嘛的呢? Vuex ...

  9. vue:vuex中mapState、mapGetters、mapActions辅助函数及Module的使用

    一.普通store中使用mapState.mapGetters辅助函数: 在src目录下建立store文件夹: ​ index.js如下: import Vue from 'vue'; import ...

随机推荐

  1. vb.net DBEntities框架联表查询 Join

    在项目中配置好DBEntities 使用两个表:主表Table, 子表Table_Item 主要是用到了委托和泛型,ForEach用的是不带返回值的委托 Sub GetDb() Dim st As N ...

  2. API接口利用ActionFilterAttribute实现接口耗时检测

    1.主要代码 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; ...

  3. java基础3(异常)

    1.异常的体系 1)请描述异常的继承体系 异常继承体系为:异常的根类是 java.lang.Throwable,其下有两个子类:java.lang.Error 与 java.util.Exceptio ...

  4. haproxy + keepalived + mycat 高可用与负载均衡集群配置 centos7

    架构如上,但是其实keepalived.haproxy.Mycat都可以多台(比如keepalived.haproxy.Mycat各3台,3台keepalived抢占vip,然后抢到vip的hapro ...

  5. Struts配置文件

    本章节将带你学习Struts2 应用程序所需的基本配置.在这里可以看到哪些将被配置到一些重要的配置文件中:web.xml.struts.xml.struts-config.xml以及struts.pr ...

  6. GraphX介绍

    转自:https://www.cnblogs.com/txq157/p/5978747.html 1.GraphX介绍 1.1 GraphX应用背景 Spark GraphX是一个分布式图处理框架,它 ...

  7. 开关灯 ToggleButton

    开关灯 ToggleButton textOn:对应true的时候:textOff:对应false的时候:给toggleButton设置监听器toggleButton.setOnCheckChange ...

  8. TextView跑马灯

    TextView跑马灯 textView跑马灯实现:1.定义textView标签的4个属性:android:singleLine="true"//使其只能单行android:ell ...

  9. 打造高效 VIM

    删除空行 删除1到10行的空行 :1,10g/^$/d 命令行快捷命令 Bang(!)命令 上一条命令:!! 使用上一条命令的所有参数:!* 使用上一条命令的最后一个参数:!$ 使用上一条命令中除了最 ...

  10. goquery 解析不了noscript

    今天在用goquery的时候 解析noscript标签的时候.发现一直获取不到里面的元素. google得到.需要去除noscript标签. s.Find("noscript"). ...