vue + ts Vuex篇
Vuex对Typescript的支持,仍十分薄弱,官方库只是添加了一些.d.ts
声明文件,并没有像vue 2.5
这样内置支持。
第三方衍生库 vuex-typescript
, vuex-ts-decorators
, vuex-typex
, vuex-class
等等,我个人的总结,除了vuex-class
外,基本都存在侵入性太强的问题,引用不算友好。而vuex-class
提供的功能其实也是薄薄一层,并不能解决核心痛点。因此,需要手动添加辅助的地方,其实颇多。
核心痛点:每次调用 this.$store.dispatch
/ this.$store.commit
/ this.$store.state
/ this.$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) {
// ...
}
}
其中第二个参数products
,payload
参数,用法同上步 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根本用不到的参数,才能得到需要的dispatch
, commit
,在我看来,难用至极。
个人更喜欢如下写法:
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
}, )
} checkout (products: CartProduct[]) {
// dispatchCheckout(products)
this.actionCheckout(products)
}
}
欢迎关注公众号,进一步技术交流:
vue + ts Vuex篇的更多相关文章
- vue+ts搭建项目
Tip: 为了避免浪费您的时间,本文符合满足以下条件的同学借鉴参考 1.本文模版不适用于小型项目,两三个页面的也没必要用vue2.对typescript.vue全家桶能够掌握和运用 此次项目模版主要涉 ...
- Vuex篇
[Vuex篇] vuex源码你学会了吗? 我来免费教你!~ 1.vuex是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的 ...
- Vue学习—— Vuex学习笔记
组件是Vue最强大的功能之一,而组件实例的作用域是相互独立的,意味着不同组件之间的数据是无法相互使用.组件间如何传递数据就显得至关重要,这篇文章主要是介绍Vuex.尽量以通俗易懂的实例讲述这其中的差别 ...
- Vue3: 如何以 Vite 创建,以 Vue Router, Vuex, Ant Design 开始应用
本文代码: https://github.com/ikuokuo/start-vue3 在线演示: https://ikuokuo.github.io/start-vue3/ Vite 创建 Vue ...
- Vue 2.0 + Vue Router + Vuex
用 Vue.js 2.x 与相配套的 Vue Router.Vuex 搭建了一个最基本的后台管理系统的骨架. 当然先要安装 node.js(包括了 npm).vue-cli 项目结构如图所示: ass ...
- Vue之Vuex
一.什么是vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.简单来说就是一个数据统一 ...
- requirejs、vue、vuex、vue-route的结合使用,您认为可行吗?
在五一节之前和一网友讨论前端技术时,对方提到vue.vue-route如果配合requirejs应用.当时的我没有想得很明白,也没能这位网友一个准确的回复,但我许诺于他五一研究后给他一个回复.本是一天 ...
- Vue中Vuex的详解与使用(简洁易懂的入门小实例)
怎么安装 Vuex 我就不介绍了,官网上有 就是 npm install xxx 之类的.(其实就是懒~~~哈哈) 那么现在就开始正文部分了 众所周知 Vuex 是什么呢?是用来干嘛的呢? Vuex ...
- vue:vuex中mapState、mapGetters、mapActions辅助函数及Module的使用
一.普通store中使用mapState.mapGetters辅助函数: 在src目录下建立store文件夹: index.js如下: import Vue from 'vue'; import ...
随机推荐
- Cannot call sendRedirect() after the response has been committed的解决办法
做一个Login Demo的时候,写了如下代码: protected void doPost(HttpServletRequest request, HttpServletResponse respo ...
- 【原创】大叔经验分享(60)hive和spark读取kudu表
从impala中创建kudu表之后,如果想从hive或spark sql直接读取,会报错: Caused by: java.lang.ClassNotFoundException: com.cloud ...
- python3爬虫图片验证码识别
# 图片验证码识别 环境安装# sudo apt-get install -y tesseract-ocr libtesseract-dev libleptonica-dev# pip install ...
- JPanel实现滚动条
之前一直用JScrollPane里面放一个JTextArea,就可以在文本框内实现滚动条. 但是最近做一个小demo,需要在JPanel中实现滚动条,就找了下资料,做好了,现在记录一下,防止以后再用到 ...
- phpstudycomposer thinkPHP5.1 使用
1.首先把php变成全局变量 2.打开phpstudy composer 的安装目录 E:\phpstudy\PHPTutorial\tools\composer 把里面的文件全部删除(或者备份一下) ...
- Odoo的 base 模型
Odoo 内核中有一个base插件模块.它提供了 Odoo 应用所需的基本功能.然后有一组内置插件模块来提供标准产品中的官方应用和功能.base模块中包含两类模型: 信息仓库(Information ...
- 注解【Annotation】、反射
注解:Annotation是从JDK5.0开始引入的新技术.Annotation的作用:如果没有注解信息处理流程,则注解毫无意义)- 不是程序本身,可以对程序作出解释.(这一点,跟注释没什么区别)- ...
- C++ 批量打开写入文件
用到了C++17的filesystem 库 说明:这个函数主要是用来处理日志中不同Thread的日志,主要目的是将不同Thread的日志写到不同的文件中 int GetThreadTime(const ...
- systemd自启动tomcat
tomcat自启动service [Unit] Description=Tomcat After=network.target [Service] Type=forking PIDFile=/usr/ ...
- centos7 搭建pxe 安装centos windows(非全自动)(这个教程测试centos6和7.2可以用,Windows各版本也可以)
yum install dhcp xinetd syslinux tftp-server httpd 编辑dhcpdb配置(192.168.0.1为本机IP) ; max-lease-time ; l ...