Vuex and Typescript

2018年9月30日,尤雨溪在medium我的博客上發佈了vue3.0的開發思路。3.0帶了了很大的變化,他講了一些改進的思路以及整個開發流程的規劃。對2.0有了全面的提高,而且源碼所有用typescript重寫,因此typescript刻不容緩。本文章翻譯自國外大牛Francesco Vitullo的文章,文章連接vue

如下爲翻譯

最近,Typescript在Javascript生態系統中變得愈來愈流行,經過這篇文章,我不想深刻研究Typescript,但我想展現一個基本方法,整合Vuex在Vue應用程序中與Typescript代碼庫集成。 此時,我假設您熟悉基本的Typescript方法以及如何在Vue應用程序中使用該語言。 若是您想查看一個基本的TS示例,我建議您查看此repo:https://github.com/Microsoft/TypeScript-Vue-Starter。ios

根據官方文檔,Vuex的定義方式以下: Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。git

因爲我對Flux和Redux有豐富的經驗,這個概念對我來講聽起來並不新鮮,因此若是你熟悉這個模式,那麼獲取它並開始使用Vuex應該不是什麼大問題。 在我看來,這種模式在處理須要擴展和提升總體生產力的應用程序時很是有用。 回到這一點,咱們如何將Vuex與Typescript結合起來?github

首先,讓咱們在index.ts中初始化並暴露store: index.ts文件vuex

// 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);
複製代碼

typescript特有的types.ts文件:typescript

// types.ts
export interface RootState {
    version: string;
}
複製代碼

這些代碼與建立一個標準Vuex store很是類似,但你應該注意到稍顯不一樣:npm

  1. 一個storeOpts變量應使用「StoreOptions」類型去建立,並將泛型類型定義爲「RootState」(定義根狀態類型)
  2. 一樣new Vuex.Store需使用RootState類型。

因爲存在這些差別,咱們須要在根Vuex實例去明肯定義這些types。 通常來講,我建議並推薦採用模塊化方法,由於將Vuex鏈接到多個組件時有不少優勢,因此我在store中放置了一個簡單的基本模塊:Profile。axios

// 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:bash

// types.ts
export interface User {
    firstName: string;
    lastName: string;
    email: string;
    phone?: string;
}

export interface ProfileState {
    user?: User;
    error: boolean;
}
複製代碼

看一下index.ts文件,您可能會注意到如下幾點:網絡

  1. State被profilestate type初始化了
  2. 在這個階段,建立和導出的模塊有點複雜:它是一個定義兩種類型的模塊:ProfileState(模塊狀態)和RootState(Vuex存儲的根狀態)

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聚合(可選)起來的和內部模塊化策略。 咱們來看看示例中的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');
        });
    }
};
複製代碼

爲了導出正確的Module類型,咱們須要將咱們actions設置爲ActionTree類型,這是Vuex中定義的類型,如:

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

這並不難以理解,它表明ActionTree的鍵對象,定義了Action的名稱,以及與之相關的Action(仍然指望Module State和根State類型)。

本例中,咱們只有一個ActionTree,它只有一個fetchData的action,它執行異步任務(從服務中檢索一些數據)並根據網絡響應提交成功或錯誤。 若是成功,將向用戶輸入有效負載。
複製代碼

接下來mutations:

// 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;
    }
};
複製代碼

Mutations與咱們爲Actions討論的相同方法,並指望由Vuex定義的MutationTree類型的變量,以下所示:

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

爲了關閉模塊的初始化,咱們也暴露了所需的getter。 在咱們的例子中,一個簡單的getter返回所選用戶的全名可能就足夠了,它結合了存儲的firstName和lastName屬性。 是的,您甚至能夠在User中使用class,但這裏只是一個基本的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>;
}
複製代碼

如今,最重要的部分:咱們如何將全部內容鏈接到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;

        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>
複製代碼

上面的例子是一個很是基本的例子。 包含template的單文件組件和暴露組件的typescript。

在該示例中,我還使用vue-class-component來使用基於類的Vue組件(也是vuex-class的依賴項)。 感謝vuex-class,可使用裝飾器來得到咱們須要的東西:State,Actions,Mutations,Getters和namespaced decorators。

咱們的組件有一些計算變量即computed,一個是由@State引入的profile,指的是Profile的狀態,另外一個是咱們在模塊中定義的getter:get email()。

此示例使用由vuex-class公開的兩個顯式裝飾器:State和Getter。

爲了訪問正確的模塊,將具備namespace做爲屬性的對象(或BindingOptions)做爲第二個參數傳遞。

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

固然,在profile的vuex中須要定義fetchData的Action,組件中才能使用

@Action('fetchData', { namespace }) fetchData: any;
複製代碼

而且在mounted中使用fetchData

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

爲了渲染正確,模板的一部分是使用先前定義的getter來顯示fullName和get email() 來獲取User的email。

<p>
    Full name: {{ fullName }}
</p>
<p>
    Email: {{ email }}
</p>
複製代碼

基本上就是這樣。還有其餘方法能夠將Vue組件與Vuex鏈接,但我相信這是一種有效的入門方式。固然,在給定的示例中存在很大的改進空間,如加強代碼的類型檢查使邏輯更加清晰,或經過更好的方式來呈現模塊的渲染。我但願你喜歡這篇文章!

以上爲翻譯內容,如有不正確之處,請不吝指出。

github上用Vue-CLI3搭了個ts+vuex小例子地址

相關文章
相關標籤/搜索