摘要:引入全面指南篇系列目錄引入全面指南引入全面指南篇前言正是我下決心引入的核心痛點(diǎn)。其中,可以通過(guò)建立輔助函數(shù)形式,簡(jiǎn)單繞開(kāi)。只是類(lèi)型均為建議不使用,以明確指定類(lèi)型及調(diào)用可通過(guò)上述下輔助函數(shù),手動(dòng)開(kāi)啟類(lèi)型推導(dǎo)及類(lèi)型推導(dǎo),暫時(shí)只能手動(dòng)指定。
Vue2.5+ Typescript 引入全面指南 - Vuex篇
系列目錄:
Vue2.5+ Typescript 引入全面指南
Vue2.5+ Typescript 引入全面指南 - Vuex篇
前言Vuex 正是我下決心引入Typescript的核心痛點(diǎn)。與 vuex 相關(guān)的代碼中,到處充斥著此般寫(xiě)法:
再加上vuex的 dispatch/commit 并非直接引用代碼,而是是通過(guò)一個(gè)string類(lèi)型的 type 來(lái)標(biāo)記,如此一來(lái)上圖中寫(xiě)法,如果想查看 payload 的具體內(nèi)容,甚至不能借助于編輯器的查找定義,只能手動(dòng)去切代碼查看!簡(jiǎn)直苦不堪言。
而借助于 typescript 的 interface 接口,至少可以簡(jiǎn)化成如下效果:
這么寫(xiě)一樣麻煩,不是還需要記類(lèi)似PostLoginParams的Interface類(lèi)型?這簡(jiǎn)單,建個(gè)輔助函數(shù)就是:
編輯器里一個(gè) Ctrl + 空格,payload里有哪些參數(shù)就全出來(lái),再也不用去一遍遍翻代碼,效率直線提升!
現(xiàn)狀概述截至當(dāng)前2017年11月,Vuex對(duì)Typescript的支持,仍十分薄弱,官方庫(kù)只是添加了一些.d.ts聲明文件,并沒(méi)有像vue 2.5這樣內(nèi)置支持。
第三方衍生庫(kù) vuex-typescript, vuex-ts-decorators, vuex-typex, vuex-class等等,我個(gè)人的總結(jié),除了vuex-class外,基本都存在侵入性太強(qiáng)的問(wèn)題,引用不算友好。而vuex-class提供的功能其實(shí)也是薄薄一層,并不能解決核心痛點(diǎn)。因此,需要手動(dòng)添加輔助的地方,其實(shí)頗多。
核心痛點(diǎn):每次調(diào)用 this.$store.dispatch / this.$store.commit / this.$store.state/ this.$store.getters 都會(huì)伴隨著類(lèi)型丟失。
其中,dispatch/commit 可以通過(guò)建立輔助函數(shù)形式,簡(jiǎn)單繞開(kāi)。 state/getters 沒(méi)有太好辦法,只能手動(dòng)指定,若覺(jué)得麻煩,可以全都指成 any,等官方支持。官方動(dòng)態(tài)見(jiàn)此 issue
動(dòng)手改造第一步:從 shopping-cart 示例搬運(yùn)代碼以下示例基于 vuex 官方 examples 中最復(fù)雜的一個(gè) shopping-cart,
改造后的完整代碼見(jiàn) vue-vuex-typescript-demo
準(zhǔn)備工作:
shopping-cart代碼復(fù)制至項(xiàng)目目錄下
.js文件統(tǒng)一重命名為.ts,
currency.js/api/shop.js/components/App.vue等外圍文件的ts改造
npm i -D vuex 添加依賴
詳細(xì)步驟這里略去,參照 代碼庫(kù) 即可
動(dòng)手改造第二步:State改造用到state變量的地方實(shí)在太多,不僅store目錄下 action/getter/mutation 均有可能需要,甚至在 .vue 文件里,mapState也有引用,因此我個(gè)人總結(jié)的一套實(shí)踐:
store/modules下的每個(gè)子模塊,均維護(hù)自己名為 State 的 Interface 聲明
store/index.ts 文件中,匯總各子模塊,維護(hù)一個(gè)總的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: State) => { return state.cart.added.map(shape => { // 此處shape自動(dòng)推導(dǎo)出Shape類(lèi)型 // ... 詳見(jiàn)源碼 }) }
如此,所有直接引用 state 的地方,均可啟用類(lèi)型推導(dǎo)
動(dòng)手改造之 MutationMutation 對(duì)應(yīng) store.commit 命令,常見(jiàn)寫(xiě)法:
const mutations = { [types.ADD_TO_CART] (state, { id }) { // ... } }
state 上步已處理{ id },payload 參數(shù),即為開(kāi)篇介紹類(lèi)型缺失的重災(zāi)區(qū)。
我的一套個(gè)人實(shí)踐:
store/modules 下的子模塊文件,為自己的mutations 維護(hù) payload Interface聲明
子模塊共用 payload(多個(gè)模塊響應(yīng)同一 commit 等),在 store/index.ts 中統(tǒng)一維護(hù)
新建文件 store/dispatches.ts 文件,為每一個(gè)直接調(diào)用的帶參commit維護(hù)輔助函數(shù),以應(yīng)用類(lèi)型推導(dǎo)
子模塊 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調(diào)用舉例: 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輔助函數(shù),參見(jiàn)下步同文件dispatch輔助函數(shù)
動(dòng)手改造之 ActionAction 對(duì)應(yīng) store.dispatch 命令,常見(jiàn)寫(xiě)法:
const actions = { checkout ({ commit, state }, products) { // ... } }
其中第二個(gè)參數(shù)products,payload 參數(shù),用法同上步 Mutation 的 payload 參數(shù),不再贅述。
第一個(gè)參數(shù){ commit, state },context參數(shù),vuex 的 d.ts 提供有類(lèi)型 ActionContext,用法如下:
import { ActionContext } from "vuex" const actions = { checkout (context: ActionContext, products: CartProduct[]) { context.commit(types.CHECKOUT_REQUEST) // ... } }
ActionContext
個(gè)人更喜歡如下寫(xiě)法:
const actions = { checkout (context: { commit: Commit, state: State }, products: CartProduct[]) { context.commit(types.CHECKOUT_REQUEST) // ... } }
Action payload 改造參見(jiàn)步驟 Mutation,不再贅述。
store/dispatches.ts文件,dispatch輔助函數(shù):
// ./src/store/dispatches.ts import store, { CartProduct, Product } from "./index" export const dispatchCheckout = (products: CartProduct[]) => { return store.dispatch("checkout", products) }
.vue文件調(diào)用舉例:
// ./src/components/Cart.vue import { dispatchCheckout } from "../store/dispatches" export default Vue.extend({ methods: { checkout (products: CartProduct[]) { // this.$store.dispatch 寫(xiě)法可用,但不帶類(lèi)型推導(dǎo) // this.$store.dispatch("checkout", products) dispatchCheckout(products) // 帶有類(lèi)型智能提示 } } })動(dòng)手改造之 Getter
Getter常見(jiàn)寫(xiě)法:
const getters = { checkoutStatus: state => state.checkoutStatus }
需要改的不多,state 加上聲明即可:
const getters = { checkoutStatus: (state: State) => state.checkoutStatus }動(dòng)手改造之獨(dú)立的 Mutations/Actions/Getters 文件
獨(dú)立文件常規(guī)寫(xiě)法:
// ./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: State) => { return state.cart.added.map(shape => { // ... }) } const getterTree: GetterTree = { cartProducts } export default getterTree
Actions/Mutations 文件改造同上,類(lèi)型換成 ActionTree, Action, MutationTree, Mutation即可
引用:
// ./src/store/index.js import getters from "./getters" export default new Vuex.Store({ getters })
原因是vuex定義,new Vuex.Store參數(shù)類(lèi)型 StoreOptions 如下:
export interface StoreOptions{ state?: S; getters?: GetterTree; actions?: ActionTree; mutations?: MutationTree; modules?: ModuleTree; plugins?: Plugin[]; strict?: boolean; }
于是,獨(dú)立Gettes/Actions/Mutations文件,export 必須是GetterTree/ActionTree/MutationTree類(lèi)型
動(dòng)手改造之 .vue 文件調(diào)用傳統(tǒng)寫(xiě)法全部兼容,只需 mapState為state添加類(lèi)型 (state: State) => state.balabal 等很少改動(dòng)即可正常運(yùn)行。只是類(lèi)型均為 any
建議不使用 mapState / mapGetters / mapActions / mapMutations,以明確指定類(lèi)型
dispatch 及 commit 調(diào)用可通過(guò)上述 store/dispatches.ts 下輔助函數(shù),手動(dòng)開(kāi)啟類(lèi)型推導(dǎo)
state 及 getters 類(lèi)型推導(dǎo),暫時(shí)只能手動(dòng)指定。自動(dòng)推導(dǎo),估計(jì)得等官方內(nèi)置支持了。
完整調(diào)用示例:
// ./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 組件式寫(xiě)法
如果覺(jué)得以上廢棄 mapState / mapGetters 后的寫(xiě)法繁瑣,可引入vue-class-component + vuex-class,開(kāi)啟組件式寫(xiě)法
vue-class-component,vue官方維護(hù),學(xué)習(xí)成本低
vuex-class,作者 ktsn,vuex及vue-class-component貢獻(xiàn)排第二(第一尤雨溪了)的活躍開(kāi)發(fā)者,質(zhì)量還是有保障的
引入這倆依賴后,須在 tsconfig.json 添加配置:
{ "compilerOptions": { // 啟用 vue-class-component 及 vuex-class 需要開(kāi)啟此選項(xiàng) "experimentalDecorators": true, // 啟用 vuex-class 需要開(kāi)啟此選項(xiàng) "strictFunctionTypes": false } }
Component 寫(xiě)法示例:
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) } }總結(jié)
在現(xiàn)階段 vuex 官方未改進(jìn) typescript 支持下,用 typescript 寫(xiě) vuex 代碼,的確有些繁瑣,而且支持也稱不上全面,不過(guò),總比沒(méi)有強(qiáng)。哪怕都用 any,也能借助智能提示減輕一些代碼翻來(lái)翻去的痛苦。
至于再進(jìn)一步更完美的支持,等官方更新吧。
完整代碼見(jiàn) Github 庫(kù):vue-vuex-typescript-demo
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/89543.html
摘要:引入全面指南系列目錄引入全面指南引入全面指南篇寫(xiě)在前面寫(xiě)這篇文章時(shí)的我,使用經(jīng)驗(yàn)三個(gè)多月,完全空白,花了大概三個(gè)晚上把手頭項(xiàng)目遷移至,因此這篇文章更像個(gè)入門(mén)指引。見(jiàn)文章引入全面指南篇完整代碼見(jiàn)庫(kù),分支為整合示例,分支為不含的基礎(chǔ)示例。 Vue2.5+ Typescript 引入全面指南 系列目錄: Vue2.5+ Typescript 引入全面指南 Vue2.5+ Typescrip...
摘要:遷移至指南為什么要遷移至本身是動(dòng)態(tài)弱類(lèi)型的語(yǔ)言,這樣的特點(diǎn)導(dǎo)致了代碼中充斥著很多的報(bào)錯(cuò),給開(kāi)發(fā)調(diào)試和線上代碼穩(wěn)定都帶來(lái)了不小的負(fù)面影響。可行性因?yàn)槭堑某?,不?huì)阻止的運(yùn)行,即使存在類(lèi)型錯(cuò)誤也不例外,這能讓你的逐步遷移至。 Vue2.5+遷移至Typescript指南 為什么要遷移至Typescript Javascript本身是動(dòng)態(tài)弱類(lèi)型的語(yǔ)言,這樣的特點(diǎn)導(dǎo)致了Javascript代...
摘要:遷移至指南為什么要遷移至本身是動(dòng)態(tài)弱類(lèi)型的語(yǔ)言,這樣的特點(diǎn)導(dǎo)致了代碼中充斥著很多的報(bào)錯(cuò),給開(kāi)發(fā)調(diào)試和線上代碼穩(wěn)定都帶來(lái)了不小的負(fù)面影響。可行性因?yàn)槭堑某?,不?huì)阻止的運(yùn)行,即使存在類(lèi)型錯(cuò)誤也不例外,這能讓你的逐步遷移至。 Vue2.5+遷移至Typescript指南 為什么要遷移至Typescript Javascript本身是動(dòng)態(tài)弱類(lèi)型的語(yǔ)言,這樣的特點(diǎn)導(dǎo)致了Javascript代碼...
摘要:哪吒別人的看法都是狗屁,你是誰(shuí)只有你自己說(shuō)了才算,這是爹教我的道理。哪吒去他個(gè)鳥(niǎo)命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰(shuí)和你做朋友太乙真人人是否能夠改變命運(yùn),我不曉得。我只曉得,不認(rèn)命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...
閱讀 1248·2023-04-25 17:05
閱讀 3046·2021-11-19 09:40
閱讀 3678·2021-11-18 10:02
閱讀 1778·2021-09-23 11:45
閱讀 3059·2021-08-20 09:36
閱讀 2817·2021-08-13 15:07
閱讀 1166·2019-08-30 15:55
閱讀 2497·2019-08-30 14:11