最近项目中用到了 感觉蛮不错的 所以分享下 本篇文章翻译Francesco Vitullo大佬的文章链接。
最近,Typescript在Javascript生态系统中变得越来越流行,通过这篇文章,我不想深入研究Typescript,但是我想展示一种基本的方法,将Vuex与Typescript代码库集成在一个Vue应用程序中。
现在,我假设您熟悉基本的Typescript方法以及如何在Vue应用程序中使用该语言。如果你想看看一个基本的TS例子,我建议你看看这个repo: https://github.com/Microsoft/TypeScript-Vue-Starter
根据官方文件,Vuex的定义如下:
Vuex是一个状态管理模式+ Vue.js应用程序库。它充当应用程序中所有组件的集中存储,并使用规则确保状态只能以可预测的方式进行更改。
因为我对Flux和Redux有丰富的经验,所以这个概念对我来说并不陌生,所以如果你熟悉这个模式,那么开始使用Vuex也没什么大不了的。
在我看来,这种模式在处理需要扩展和提高整体生产力的应用程序时非常有用。
言归正传,我们如何将Vuex与Typescript结合起来?
- 首先,让我们在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 = {
state: {
version: '1.0.0' // a simple property
},
modules: {
profile
}
};
export default new Vuex.Store(store);
- types.ts:
// types.ts
export interface RootState {
version: string;
}
这些代码与创建一个标准Vuex store非常相似,但你应该注意到稍显不同:
- 使用“StoreOptions”类型创建storeOpts变量,并将泛型类型定义为“RootState”(它定义根状态类型)
- 新的Vuex。Store也使用了RootState类型
由于这些差异,我们明确地定义了根Vuex实例的类型。
与往常一样,我建议并推荐采用模块化方法,因为在将Vuex连接到多个组件时有许多优点,所以我用一个简单而基本的模块布置了存储: 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 = {
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文件,你可能会注意到以下几点:
- 状态正初始化为ProfileState类型
- 在这个阶段,创建和导出模块要复杂一些:它是一个定义了两种类型的模块:ProfileState(即模块状态)和RootState (Vuex存储的根状态)
- Module是Vuex声明的interface文件
// vuex/types/index.d.ts
export interface Module {
namespaced?: boolean;
state?: S | (() => S);
getters?: GetterTree;
actions?: ActionTree;
mutations?: MutationTree;
modules?: ModuleTree;
}
看一下暴露类型,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 = {
fetchData({ commit }): any {
axios({
url: 'https://....'
}).then((response) => {
const payload: User = response && response.data;
commit('profileLoaded', payload);
}, (error) => {
console.log(error);
commit('profileError');
});
}
};
为了导出Vuex的模块类型所期望的内容,我们需要将我们的动作聚合到一个“ActionTree”中,Vuex中定义了如下类型:
// vuex/types/index.d.ts
export interface ActionTree {
[key: string]: Action;
}
这没什么好理解的,它表示一个需要一些键的对象,定义动作的名称,以及一个与之相关的动作(仍然需要模块状态和根状态类型)
在我们的例子中,我们只有一个ActionTree,其中只包含一个名为“fetchData”的简单操作,它执行异步任务(从服务中检索一些数据),并根据网络响应提交成功或错误。如果成功,则将有效负载类型设置为User。
- Mutations.ts
// profile/mutations.ts
import { MutationTree } from 'vuex';
import { ProfileState, User } from './types';
export const mutations: MutationTree = {
profileLoaded(state, payload: User) {
state.error = false;
state.user = payload;
},
profileError(state) {
state.error = true;
state.user = undefined;
}
};
突变是遵循相同的方法,我们讨论的行动和预期的变量突变树类型由Vuex定义如下:
// vuex/types/index.d.ts
export interface MutationTree {
[key: string]: Mutation;
}
为了结束模块的初始化,我们还公开了所需的getter。在我们的例子中,一个简单的getter返回所选用户的全名就足够了,它结合了存储的firstName和lastName属性。
是的,你甚至可以为用户用一个类来做这个,但是我想要为getter也有一个基本的例子。
- Getters.ts:
// profile/getters.ts
import { GetterTree } from 'vuex';
import { ProfileState } from './types';
import { RootState } from '../types';
export const getters: GetterTree = {
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 {
[key: string]: Getter;
}
现在,有趣的部分是:如何将所有内容连接到一个Vue组件?
对于下面的示例,我使用 vuex-class将一个简单的组件连接到Vuex。
Full name: {{ fullName }}
Email: {{ email }}
Oops an error occured
上面的例子是一个非常基本的例子。一个单独的文件组件,包含“模板”(当定义的条件在逻辑上变为true时,使用一个粗略的策略来显示正确的部分)和暴露组件的“脚本”。在这个例子中,我还使用vue-class-component来使用基于类的Vue组件(也是vuex-class的一个依赖项)。
由于Vuex-class,我们可以使用decorator来获得我们需要的任何东西:状态、操作、突变、getter和包装“有名称空间的decorator”。
我们的组件将有两个计算变量,一个名为“profile”,指的是概要文件的状态,另一个指的是我们在模块中定义的“getter”。
这个例子使用了两个由vuex-class公开的显式装饰器:State和Getter。为了访问正确的模块,将“namespace”作为属性的对象(或BindingOptions)作为第二个参数传递。
@State('profile') profile: ProfileState;
@Getter('fullName', { namespace }) fullName: string;
在我们的例子中,我们需要将动作“fetchData”与动作装饰器连接起来:
@Action('fetchData', { namespace }) fetchData: any;
并在“挂载”的生命周期回调中执行:
mounted() {
// fetching data as soon as the component's been mounted
this.fetchData();
}
要呈现一些有意义的内容,模板的一部分是使用前面检查过的getter来呈现“fullName”和一个基本的计算属性来获取用户的电子邮件。
Full name: {{ fullName }}
Email: {{ email }}
基本上就是这样。还有其他方法来连接一个Vue组件与Vuex,但我相信这是一个有效的方式开始。
当然,在给定的示例/代码中还有很多改进的空间,例如,增强代码的类型以获得更健壮的逻辑或更好的方式来呈现模块的更改。
我希望你喜欢这篇文章!