vue + ts Vuex篇

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 下文件举例:

// ./src/store/modules/cart.ts

interface Shape {
  id: number
  quantity: number
}

export interface State {
  added: Shape[]
  checkoutStatus: ‘successful‘ | ‘failed‘ | null
}

// initial state
// shape: [{ id, quantity }]
const state: State = {
  added: [],
  checkoutStatus: null
}

// 需引用state的地方举例:

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

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

// ./src/store/index.ts

import { State as CardState } from ‘./modules/cart‘
import { State as ProductsState } from ‘./modules/products‘

export interface State {
  cart: CardState,
  products: ProductsState
}

总 State 引用示例:

// ./src/store/getters.ts

import { State } from ‘./index‘

const cartProducts: Getter<State, any> = (state: State) => {
  return state.cart.added.map(shape => {
    // 此处shape自动推导出Shape类型
    // ... 详见源码
  })
}

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

动手改造之 Mutation

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

const mutations = {
  [types.ADD_TO_CART] (state, { id }) {
    // ...
  }
}

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

我的一套个人实践:

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

子模块 payload 声明举例:

// ./src/store/modules/products.ts

import { Product, AddToCartPayload } from ‘../index‘

export interface ProductsPayload {
  products: Product[]
}

const mutations = {
  [types.RECEIVE_PRODUCTS] (state: State, payload: ProductsPayload) {
    state.all = payload.products
  },

  [types.ADD_TO_CART] (state: State, payload: AddToCartPayload) {
    const product = state.all.find(p => p.id === payload.id)
    // ...
  }
}

// mutations调用举例:
const actions = {
  getAllProducts (context: ActionContextBasic) {
    shop.getProducts((products: Product[]) => {
      const payload: ProductsPayload = {
        products
      }
      context.commit(types.RECEIVE_PRODUCTS, payload)
    })
  }
}

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

// ./src/store/index.ts

export interface AddToCartPayload {
  id: number
}

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

动手改造之 Action

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

const actions = {
  checkout ({ commit, state }, products) {
    // ...
  }
}

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

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

import { ActionContext } from ‘vuex‘
const actions = {
  checkout (context: ActionContext<State, any>, products: CartProduct[]) {
    context.commit(types.CHECKOUT_REQUEST)
    // ...
  }
}

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

个人更喜欢如下写法:

const actions = {
  checkout (context: { commit: Commit, state: State }, products: CartProduct[]) {
    context.commit(types.CHECKOUT_REQUEST)
    // ...
  }
}

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

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

// ./src/store/dispatches.ts

import store, { CartProduct, Product } from ‘./index‘

export const dispatchCheckout = (products: CartProduct[]) => {
  return store.dispatch(‘checkout‘, products)
}

.vue文件调用举例:

// ./src/components/Cart.vue

import { dispatchCheckout } from ‘../store/dispatches‘
export default Vue.extend({
  methods: {
    checkout (products: CartProduct[]) {
    // this.$store.dispatch 写法可用,但不带类型推导
    // this.$store.dispatch(‘checkout‘, products)
    dispatchCheckout(products) // 带有类型智能提示
    }
  }
})

动手改造之 Getter

Getter常见写法:

const getters = {
  checkoutStatus: state => state.checkoutStatus
}

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

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

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

独立文件常规写法:

// ./src/store/getters.js
export const cartProducts = state => {
  return state.cart.added.map(({ id, quantity }) => {
    const product = state.products.all.find(p => p.id === id)
    return {
      title: product.title,
      price: product.price,
      quantity
    }
  })
}

引用:

// ./src/store/index.js

import * as getters from ‘./getters‘
export default new Vuex.Store({
  getters
})

typescript下均需改造:

// ./src/
import { GetterTree, Getter } from ‘vuex‘
import { State } from ‘./index‘

const cartProducts: Getter<State, any> = (state: State) => {
  return state.cart.added.map(shape => {
    // ...
  })
}

const getterTree: GetterTree<State, any> = {
  cartProducts
}

export default getterTree

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

引用:

// ./src/store/index.js

import getters from ‘./getters‘
export default new Vuex.Store({
  getters
})

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

export interface StoreOptions<S> {
  state?: S;
  getters?: GetterTree<S, S>;
  actions?: ActionTree<S, S>;
  mutations?: MutationTree<S>;
  modules?: ModuleTree<S>;
  plugins?: Plugin<S>[];
  strict?: boolean;
}

于是,独立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 类型推导,暂时只能手动指定。自动推导,估计得等官方内置支持了。

完整调用示例:

// ./src/components/ProductList.vue

import Vue from ‘vue‘
// import { mapGetters, mapActions } from ‘vuex‘
import { Product } from ‘../store‘
import { dispatchAddToCart } from ‘../store/dispatches‘

export default Vue.extend({
  computed: {
    // ...mapGetters({
    //   products: ‘allProducts‘
    // })
    products (): Product[] {
      return this.$store.getters.allProducts
    }
  },
  methods: {
    // ...mapActions([
    //   ‘addToCart‘
    // ])
    addToCart (p: Product) {
      dispatchAddToCart(p)
    }
  },
  created () {
    this.$store.dispatch(‘getAllProducts‘)
  }
})

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 添加配置:

{
  "compilerOptions": {
    // 启用 vue-class-component 及 vuex-class 需要开启此选项
    "experimentalDecorators": true,

    // 启用 vuex-class 需要开启此选项
    "strictFunctionTypes": false
  }
}

Component 写法示例:

import Vue from ‘vue‘
import { Product } from ‘../store‘
// import { dispatchAddToCart } from ‘../store/dispatches‘
import Component from ‘vue-class-component‘
import { Getter, Action } from ‘vuex-class‘

@Component
export default class Cart extends Vue {
  @Getter(‘cartProducts‘) products: CartProduct[]
  @Getter(‘checkoutStatus‘) checkoutStatus: CheckoutStatus
  @Action(‘checkout‘) actionCheckout: Function

  get total (): number {
    return this.products.reduce((total, p) => {
      return total + p.price * p.quantity
    }, 0)
  }

  checkout (products: CartProduct[]) {
    // dispatchCheckout(products)
    this.actionCheckout(products)
  }
}

原文地址:https://www.cnblogs.com/cczlovexw/p/11551768.html

时间: 2024-11-09 09:42:33

vue + ts Vuex篇的相关文章

Vuex篇

[Vuex篇] vuex源码你学会了吗? 我来免费教你!~ 1.vuex是什么?  Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试.状态快照导入导出等高级调试功能. 2.什么是“状态管理模式”? 让我们从一个简单的 Vue 计数应用开始: new Vue({ 

Vue第三篇

Vue第三篇 复习 """ v-if | v-show "tag == 0" v-if | v-else-if | v-else v-for="obj in objs" <div :abc="obj">{{ obj }}</div> computed:监听绑定函数中的所有变量,返回值给绑定的变量 watch:监听绑定的变量 let localTag = { # => <local-

vue+ts搭建项目

Tip: 为了避免浪费您的时间,本文符合满足以下条件的同学借鉴参考 1.本文模版不适用于小型项目,两三个页面的也没必要用vue2.对typescript.vue全家桶能够掌握和运用 此次项目模版主要涉及的技术框架: vue2.5 vuex3.0 vue-router3.0 axios typescript3.2 Tip: 由于vue-cli3.0帮我们简化了webpack的配置,我们只需要在根目录下的vue.config.js文件的chainWebpack进行配置即可. 接下来进入正题(前提是你

vue-cli3 + ts + vuex

推荐使用vuex-class 官方介绍 npm install --save vuex-class # or yarn add vuex-class 本文也围绕着vuex-class进行介绍(目前我就会这一种)主要介绍取值和赋值使用方法说明如下: store.ts中的写法: import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); export default new Vuex.Store({ state: { count:0

【09】Vue 之 Vuex

9.1. 引言 Vue组件化做的确实非常彻底,它独有的vue单文件组件也是做的非常有特色.组件化的同时带来的是:组件之间的数据共享和通信的难题. 尤其Vue组件设计的就是,父组件通过子组件的prop进行传递数据,而且数据传递是单向的.也就是说:父组件可以把数据传递给子组件,但是 反之则不同.如下图所示: 9.2. 单向数据流动 单方向的数据流动带来了非常简洁和清晰的数据流,纯展示性或者独立性较强的模块的开发确实非常方便和省事. 但是复杂的页面逻辑,组件之间的数据共享处理就会需要通过事件总线的方式

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

在五一节之前和一网友讨论前端技术时,对方提到vue.vue-route如果配合requirejs应用.当时的我没有想得很明白,也没能这位网友一个准确的回复,但我许诺于他五一研究后给他一个回复.本是一天的研究却被我搞成了研究了一周,这拖延症... 闲话少数,进入正题 一.示例代码说明 代码结构说明: modules(存放的为组件,主要是模板与Js对象分成两个文件来写) route:测试子路由的组件 title:就是一个简单的显示文字的组件 app.js:核心类,提供vue的创建.以前modules

一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app

一步一步教你用 Vue.js + Vuex 制作专门收藏微信公众号的 app 转载 作者:jrainlau 链接:https://segmentfault.com/a/1190000005844155 项目地址:https://github.com/jrainlau/wechat-subscriptor 下载&运行 git clone git@github.com:jrainlau/wechat-subscriptor.git cd wechat-subscriptor && np

vue学习---vuex之简介

每一个 Vuex 应用的核心就是 store(仓库)."store" 基本上就是一个容器,它包含着你的应用中大部分的状态(state).Vuex 和单纯的全局对象有以下两点不同: Vuex 的状态存储是响应式的.当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新. 你不能直接改变 store 中的状态.改变 store 中的状态的唯一途径就是显式地提交(commit) mutations. 下面就实现一个计数功能简介

基于VUE+TS中引用ECharts的中国地图和世界地图密度表

首先先附上官网 http://echarts.baidu.com/option.html#series-line.label 以及密度表对应geo配置文档 http://echarts.baidu.com/option.html#geo 以下仅是个人在开发中逐步摸索出来的.demo目前没出问题.如果有错误地方请留言指出  (若转载请标注出处) 直接上效果图,对应代码在效果图下面 安装: 1. npm install echarts --save2. npm install --save @typ