当前位置: 首页 > news >正文

Vuex and Typescript

2018年9月30日,尤雨溪在medium个人博客上发布了vue3.0的开发思路。3.0带了了很大的变化,他讲了一些改进的思路以及整个开发流程的规划。对2.0有了全面的提升,并且源码全部用typescript重写,所以typescript刻不容缓。本文章翻译自国外大牛Francesco Vitullo的文章,文章链接。

以下为翻译

最近,Typescript在Javascript生态系统中变得越来越流行,通过这篇文章,我不想深入研究Typescript,但我想展示一个基本方法,整合Vuex在Vue应用程序中与Typescript代码库集成。 此时,我假设您熟悉基本的Typescript方法以及如何在Vue应用程序中使用该语言。 如果您想查看一个基本的TS示例,我建议您查看此repo:https://github.com/Microsoft/TypeScript-Vue-Starter。

根据官方文档,Vuex的定义方式如下: Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

由于我对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<RootState> = {
    state: {
        version: '1.0.0' // a simple property
    },
    modules: {
        profile
    }
};
export default new Vuex.Store<RootState>(store);
复制代码

typescript特有的types.ts文件:

// types.ts
export interface RootState {
    version: string;
}
复制代码

这些代码与创建一个标准Vuex store非常相似,但你应该注意到稍显不同:

  1. 一个storeOpts变量应使用“StoreOptions”类型去创建,并将泛型类型定义为“RootState”(定义根状态类型)
  2. 同样new Vuex.Store需使用RootState类型。

由于存在这些差异,我们需要在根Vuex实例去明确定义这些types。 一般来说,我建议并推荐采用模块化方法,因为将Vuex连接到多个组件时有很多优点,所以我在store中放置了一个简单的基本模块: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文件,您可能会注意到以下几点:

  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小例子地址

转载于:https://juejin.im/post/5c46c625e51d456e4138fa82

相关文章:

  • shell日志搜索命令
  • 毕玄:我在阿里的十年技术感悟
  • NLPIR智能语义技术让大数据挖掘更简单
  • 开源Pravega架构解析:如何通过分层解决流存储的三大挑战?
  • Android AutoCompleteTextView和MultiAutocompleteTextView实现动态自动匹配输入的内容
  • socketserver
  • Python Revisited Day 04 (控制结构与函数)
  • 开源引路人:我的Apache Mentor之路
  • 一次性能优化:吞吐量从1提升到2500
  • Eureka自我保护机制与Eureka服务发现(Discovery)
  • __proto__ 和 prototype的关系
  • 这道js题你会吗?
  • java B2B2C源码电子商城系统-Spring Cloud Eureka自我保护机制
  • 基于 React TypeScript Webpack 的微前端应用模板
  • xgboost回归损失函数自定义【一】
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • ➹使用webpack配置多页面应用(MPA)
  • FastReport在线报表设计器工作原理
  • JavaScript 无符号位移运算符 三个大于号 的使用方法
  • JAVA之继承和多态
  • nfs客户端进程变D,延伸linux的lock
  • React Transition Group -- Transition 组件
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 思考 CSS 架构
  • 微信支付JSAPI,实测!终极方案
  • 项目实战-Api的解决方案
  • Java性能优化之JVM GC(垃圾回收机制)
  • MyCAT水平分库
  • #13 yum、编译安装与sed命令的使用
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (二十四)Flask之flask-session组件
  • (三) diretfbrc详解
  • (生成器)yield与(迭代器)generator
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (学习日记)2024.01.09
  • (转)Linq学习笔记
  • (转载)Linux网络编程入门
  • .htaccess配置常用技巧
  • .Net 8.0 新的变化
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .net2005怎么读string形的xml,不是xml文件。
  • .Net8 Blazor 尝鲜
  • .NET开源项目介绍及资源推荐:数据持久层
  • .NET企业级应用架构设计系列之应用服务器
  • .skip() 和 .only() 的使用
  • /proc/interrupts 和 /proc/stat 查看中断的情况
  • ??myeclipse+tomcat
  • @AliasFor注解
  • @ConditionalOnProperty注解使用说明
  • @data注解_一枚 架构师 也不会用的Lombok注解,相见恨晚
  • [ element-ui:table ] 设置table中某些行数据禁止被选中,通过selectable 定义方法解决
  • [《百万宝贝》观后]To be or not to be?