vuex + Typescript

最近項目中用到了 感覺蠻不錯的 所以分享下 本篇文章翻譯Francesco Vitullo大佬的文章鏈接

最近,Typescript在Javascript生態(tài)系統(tǒng)中變得越來越流行,通過這篇文章,我不想深入研究Typescript,但是我想展示一種基本的方法,將Vuex與Typescript代碼庫集成在一個Vue應(yīng)用程序中。
現(xiàn)在,我假設(shè)您熟悉基本的Typescript方法以及如何在Vue應(yīng)用程序中使用該語言。如果你想看看一個基本的TS例子,我建議你看看這個repo: https://github.com/Microsoft/TypeScript-Vue-Starter

根據(jù)官方文件,Vuex的定義如下:

Vuex是一個狀態(tài)管理模式+ Vue.js應(yīng)用程序庫。它充當(dāng)應(yīng)用程序中所有組件的集中存儲,并使用規(guī)則確保狀態(tài)只能以可預(yù)測的方式進(jìn)行更改。


因為我對Flux和Redux有豐富的經(jīng)驗,所以這個概念對我來說并不陌生,所以如果你熟悉這個模式,那么開始使用Vuex也沒什么大不了的。
在我看來,這種模式在處理需要擴(kuò)展和提高整體生產(chǎn)力的應(yīng)用程序時非常有用。
言歸正傳,我們?nèi)绾螌uex與Typescript結(jié)合起來?

  • 首先,讓我們在index.ts中初始化并暴露store: index.ts文件
// index.ts
import Vue from 'vue';
import Vuex, { StoreOptions } from 'vuex';
import { RootState } from './types';
import { profile } from './profile/index';

Vue.use(Vuex);

const store: StoreOptions<RootState> = {
    state: {
        version: '1.0.0' // a simple property
    },
    modules: {
        profile
    }
};

export default new Vuex.Store<RootState>(store);
  • types.ts:
// types.ts
export interface RootState {
    version: string;
}

這些代碼與創(chuàng)建一個標(biāo)準(zhǔn)Vuex store非常相似,但你應(yīng)該注意到稍顯不同:

  • 使用“StoreOptions”類型創(chuàng)建storeOpts變量,并將泛型類型定義為“RootState”(它定義根狀態(tài)類型)
  • 新的Vuex。Store也使用了RootState類型

由于這些差異,我們明確地定義了根Vuex實例的類型。
與往常一樣,我建議并推薦采用模塊化方法,因為在將Vuex連接到多個組件時有許多優(yōu)點,所以我用一個簡單而基本的模塊布置了存儲: Profile

// profile/index.ts
import { Module } from 'vuex';
import { getters } from './getters';
import { actions } from './actions';
import { mutations } from './mutations';
import { ProfileState } from './types';
import { RootState } from '../types';

export const state: ProfileState = {
    user: undefined,
    error: false
};

const namespaced: boolean = true;

export const profile: Module<ProfileState, RootState> = {
    namespaced,
    state,
    getters,
    actions,
    mutations
};
  • types.ts
// types.ts
export interface User {
    firstName: string;
    lastName: string;
    email: string;
    phone?: string;
}

export interface ProfileState {
    user?: User;
    error: boolean;
}

看一下index.ts文件,你可能會注意到以下幾點:

  • 狀態(tài)正初始化為ProfileState類型
  • 在這個階段,創(chuàng)建和導(dǎo)出模塊要復(fù)雜一些:它是一個定義了兩種類型的模塊:ProfileState(即模塊狀態(tài))和RootState (Vuex存儲的根狀態(tài))
  • Module是Vuex聲明的interface文件
// vuex/types/index.d.ts
export interface Module<S, R> {
  namespaced?: boolean;
  state?: S | (() => S);
  getters?: GetterTree<S, R>;
  actions?: ActionTree<S, R>;
  mutations?: MutationTree<S>;
  modules?: ModuleTree<R>;
}

看一下暴露類型,Module是一個簡單的對象,將actions / mutation / getters / state聚合(可選)起來的和內(nèi)部模塊化策略。
讓我們來看看示例中的Actions。

  • Actions.ts
// profile/actions.ts
import { ActionTree } from 'vuex';
import axios from 'axios';
import { ProfileState, User } from './types';
import { RootState } from '../types';


export const actions: ActionTree<ProfileState, RootState> = {
    fetchData({ commit }): any {
        axios({
            url: 'https://....'
        }).then((response) => {
            const payload: User = response && response.data;
            commit('profileLoaded', payload);
        }, (error) => {
            console.log(error);
            commit('profileError');
        });
    }
};

為了導(dǎo)出Vuex的模塊類型所期望的內(nèi)容,我們需要將我們的動作聚合到一個“ActionTree”中,Vuex中定義了如下類型:

// vuex/types/index.d.ts
export interface ActionTree<S, R> {
  [key: string]: Action<S, R>;
}

這沒什么好理解的,它表示一個需要一些鍵的對象,定義動作的名稱,以及一個與之相關(guān)的動作(仍然需要模塊狀態(tài)和根狀態(tài)類型)
在我們的例子中,我們只有一個ActionTree,其中只包含一個名為“fetchData”的簡單操作,它執(zhí)行異步任務(wù)(從服務(wù)中檢索一些數(shù)據(jù)),并根據(jù)網(wǎng)絡(luò)響應(yīng)提交成功或錯誤。如果成功,則將有效負(fù)載類型設(shè)置為User。

  • Mutations.ts
// profile/mutations.ts
import { MutationTree } from 'vuex';
import { ProfileState, User } from './types';

export const mutations: MutationTree<ProfileState> = {
    profileLoaded(state, payload: User) {
        state.error = false;
        state.user = payload;
    },
    profileError(state) {
        state.error = true;
        state.user = undefined;
    }
};

突變是遵循相同的方法,我們討論的行動和預(yù)期的變量突變樹類型由Vuex定義如下:

// vuex/types/index.d.ts
export interface MutationTree<S> {
  [key: string]: Mutation<S>;
}

為了結(jié)束模塊的初始化,我們還公開了所需的getter。在我們的例子中,一個簡單的getter返回所選用戶的全名就足夠了,它結(jié)合了存儲的firstName和lastName屬性。
是的,你甚至可以為用戶用一個類來做這個,但是我想要為getter也有一個基本的例子。

  • Getters.ts:
// profile/getters.ts
import { GetterTree } from 'vuex';
import { ProfileState } from './types';
import { RootState } from '../types';

export const getters: GetterTree<ProfileState, RootState> = {
    fullName(state): string {
        const { user } = state;
        const firstName = (user && user.firstName) || '';
        const lastName = (user && user.lastName) || '';
        return `${firstName} ${lastName}`;
    }
};

Vuex定義如下:

// vuex/types/index.d.ts
export interface GetterTree<S, R> {
  [key: string]: Getter<S, R>;
}

現(xiàn)在,有趣的部分是:如何將所有內(nèi)容連接到一個Vue組件?
對于下面的示例,我使用 vuex-class將一個簡單的組件連接到Vuex。

<template>
    <div class="container">
        <div v-if="profile.user">
            <p>
                Full name: {{ fullName }}
            </p>
            <p>
                Email: {{ email }}
            </p>
        </div>
        <div v-if="profile.error">
            Oops an error occured
        </div>
    </div>
</template>

<script lang="ts">
    import Vue from 'vue';
    import { State, Action, Getter } from 'vuex-class';
    import Component from 'vue-class-component';
    import { ProfileState, User } from './store/profile/types';
    const namespace: string = 'profile';
@Component
    export default class UserDetail extends Vue {
        @State('profile') profile: ProfileState;
        @Action('fetchData', { namespace }) fetchData: any;
        @Getter('fullName', { namespace }) fullName: string;
        // @userModule.Mutation("changeLoginRegister") public changeLoginRegister!: Function;
        mounted() {
            // fetching data as soon as the component's been mounted
            this.fetchData();
        }

        // computed variable based on user's email
        get email() {
            const user = this.profile && this.profile.user;
            return (user && user.email) || '';
        }
    }
</script>

上面的例子是一個非常基本的例子。一個單獨(dú)的文件組件,包含“模板”(當(dāng)定義的條件在邏輯上變?yōu)閠rue時,使用一個粗略的策略來顯示正確的部分)和暴露組件的“腳本”。在這個例子中,我還使用vue-class-component來使用基于類的Vue組件(也是vuex-class的一個依賴項)。
由于Vuex-class,我們可以使用decorator來獲得我們需要的任何東西:狀態(tài)、操作、突變、getter和包裝“有名稱空間的decorator”。
我們的組件將有兩個計算變量,一個名為“profile”,指的是概要文件的狀態(tài),另一個指的是我們在模塊中定義的“getter”。
這個例子使用了兩個由vuex-class公開的顯式裝飾器:State和Getter。為了訪問正確的模塊,將“namespace”作為屬性的對象(或BindingOptions)作為第二個參數(shù)傳遞。

@State('profile') profile: ProfileState;
@Getter('fullName', { namespace }) fullName: string;

在我們的例子中,我們需要將動作“fetchData”與動作裝飾器連接起來:

@Action('fetchData', { namespace }) fetchData: any;

并在“掛載”的生命周期回調(diào)中執(zhí)行:

mounted() {
    // fetching data as soon as the component's been mounted
    this.fetchData();
}

要呈現(xiàn)一些有意義的內(nèi)容,模板的一部分是使用前面檢查過的getter來呈現(xiàn)“fullName”和一個基本的計算屬性來獲取用戶的電子郵件。

<p>
    Full name: {{ fullName }}
</p>
<p>
    Email: {{ email }}
</p>

基本上就是這樣。還有其他方法來連接一個Vue組件與Vuex,但我相信這是一個有效的方式開始。
當(dāng)然,在給定的示例/代碼中還有很多改進(jìn)的空間,例如,增強(qiáng)代碼的類型以獲得更健壯的邏輯或更好的方式來呈現(xiàn)模塊的更改。
我希望你喜歡這篇文章!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • vue+typescript之后,vuex調(diào)用再也不是從前的調(diào)用方式了,需要加類型校驗了 對比vuex在使用ts前...
    zackxizi閱讀 5,043評論 0 2
  • Typescript 作為 Javascript 的超集,越來越流行,是前端未來的一種趨勢。Vue 早已經(jīng)支持了 ...
    摸摸大海參閱讀 3,873評論 0 1
  • 九點半到幾點結(jié)束幫不到你煩惱華東交大減肥計劃等哈
    山哥_71ec閱讀 199評論 0 0
  • 陽光拍打著后背與雙眸影子催著雙腿,拉長的步伐多情的衣袖,攢滿一汪花香從頭澆下,隔岸的深情對望 擺渡的人與帆,尤自在...
    夏爅閱讀 423評論 12 13
  • 最近幾個月,從七月份開始吧,每月都要出門。四月份去的鄭州,五月是泉州,現(xiàn)在是六月中旬,還在南寧。 南寧是個挺潮的城...
    噼里啪啦小寶閱讀 167評論 0 1