阅读 3964

在Vue中使用TypeScript

原文链接: zhuanlan.zhihu.com

很早以前,想在Vue中使用TypeScript来增强开发体验,TypeScript的优点自不必多说。查了很多资料,了解了当时在Vue中使用TypeScript的体验。由于当时TypeScript对Vue支持不够,无法推断出vue实例中的'this',遂搁置了这种想法。在Vue2.5及TypeScript2.4发布之后,TypeScript对Vue的支持加强,'this'的问题得到了解决,终于能在Vue中愉快的使用TypeScript。

如果想在React中使用TypeScript,可以借助create-react-app-typescript脚手架工具,默认创建基于TypeScript的React应用,开发体验也很棒。跟React不同,目前Vue的官方脚手架Vue-cli工具还没有默认提供对TypeScript的支持,想了解更多的信息,可以看一下这个 issue。

既然Vue-cli没有提供默认的支持,那我们就自己来配置,相信对于一个合格的前端来说,配置开发环境是必备的技能......其实网上有很多相关的配置教程,那就简单的过一下吧。

1 初始化vue-cli项目,安装typescript,ts-loader,tslint,tslint-loader,tslint-config-standard,vue-property-decorator.上面只有typescript,ts-loader是必须的,其余的包用于增强开发体验。

2 增加tsconfig.json,下面是我的配置(值得注意的是,建议开启严格模式,也就是下面的strit为true这个选项,这样才能严格的推断Vue中的property):

{
  "include": [
    "./src/**/*"
  ],
  "exclude": [
    "node_modules"
  ],
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "strict": true,
    "allowJs": true,
    "module": "es2015",
    "target": "es5",
    "moduleResolution": "node",
    "isolatedModules": true,
    "lib": [
      "dom",
      "es5",
      "es2015.promise"
    ],
    "sourceMap": true,
    "pretty": true
  }
}
复制代码

2 在webpack.base.conf.js中需要配置对ts和tsx的支持(加了注释的地方是新增的或需要修改的配置):

   resolve: {
    extensions: ['.js', '.vue', '.json', 'ts', 'tsx'], // 新增了'ts', 'tsx'
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  module: {
    rules: [
      {
        test: /\.ts$/,  // 用于加载项目中的ts文件
        exclude: /node_modules/,
        enforce: 'pre',
        loader: 'tslint-loader'
      },
      {
        test: /\.tsx?$/, // 用于加载项目中的tsx文件
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          appendTsSuffixTo: [/\.vue$/],
        }
      }]
复制代码

3 将main.js重命名为main.ts, 并在webpack.base.conf.js中修改路径名

 entry: {
    app: './src/main.ts'
  }
复制代码

4 在src目录下新增vue-shim.d.ts,增加对Vue的声明:

declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}
复制代码

经过上面的配置,基本的开发环境就搞定了,接下来就可以开始写代码。为了提升开发体验,这里使用了vue-property-decorator这个包,其主要特点是封装了vue-class-component这个官方包,提供7个装饰器,通过继承Vue的方式来创建实例,大致是下面这样的:

<script lang="ts">
  import {Vue, Component, Prop, Emit, Watch} from 'vue-property-decorator';
  import CommonModal from './CommonModal.vue';

  @Component({
    components: {
      Foo
    }
  })
  export default class HelloWorld extends Vue{

    @Prop({
      type: String,
      required: true
    })
    childProp: string|number;

    tasks: Array<number|string> = [];

    task: string|number = '';

    addTask() {
      this.tasks.push(this.task);
      this.task = '';
    }

    @Watch('task')
    onTaskChange(oldValue: string|number, newValue: string|number) {
      console.log(`oldValue ${oldValue}`);
      console.log(`newValue ${newValue}`);
    }

    @Emit('handleClick')
    // 函数的参数值即是emit的payload
    emitTasks(tasks: Array<string|number>) {
      this.tasks[0] = 'haha';
    }
  }
</script>
复制代码

从上面的例子可以大致看出新的开发方式。这里需要注意的是,需要在script中加入lang='ts'。因为使用了vue-property-decorator,所以在vue的实例中写属性的时候跟以前有些不同。首先,需要在Component这个装饰器的参数中包含需要引用的组件;其次,使用emit的时候,像父组件传递的参数即是函数的参数,就像上面的tasks;父组件向子组件传递的属性,需要使用Prop这个装饰器进行声明等等。

虽然目前TypeScript对Vue支持比较好,但是Vue中的状态管理工具Vuex还不完善,所以我们可以使用vuex-class这个三方工具包来增强。其提供了State,Getter, Action,Mutation,nam-espace这几个装饰器,具体做法可以参考它的官方实例,代码如下:

import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'

const ModuleGetter = namespace('path/to/module', Getter)

@Component
export class MyComp extends Vue {
  @State('foo') stateFoo
  @State(state => state.bar) stateBar
  @Getter('foo') getterFoo
  @Action('foo') actionFoo
  @Mutation('foo') mutationFoo
  @ModuleGetter('foo') moduleGetterFoo

  // If the argument is omitted, use the property name
  // for each state/getter/action/mutation type
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux

  created () {
    this.stateFoo // -> store.state.foo
    this.stateBar // -> store.state.bar
    this.getterFoo // -> store.getters.foo
    this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
    this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
    this.moduleGetterFoo // -> store.getters['path/to/module/foo']
  }
}
复制代码

有了Vuex-class的加持,Vue在状态管理方面有了明显的提升。下面会简单的介绍一下在使用了TypeScript之后,如何来管理状态。

我的做法是,为每一个后端API 接口创建一个TypeScript的interface,然后通过这个interface来定义Vuex中的state,就像这样:

// stateType.js
export interface USERINFO {
  userId: number;
  userName: string;
  avatar: string
}

export default interface STATE {
  userInfo: USERINFO;
}

// state.js
import STATE from './stateType.js';

const state: STATE = {
  userInfo: {
    userId: 0,
    userName: '',
    avatar: '',
  }
};

export default state;
复制代码

这样做的好处是,通过interface来检查后台返回的数据结构是否满足要求,同时也可以在前端使用数据渲染页面的时候做一定的限制。当需要进行重构时,只需要修改state对应的interface,然后就能一路顺畅的该下去,非常爽。

目前使用TypeScript重构的Vue项目已经正式上线,维护性大大提升,感觉好极了。

参考资源:

kaorun343/vue-property-decorator
ktsn/vuex-class
Vue2.5+ Typescript 引入全面指南 - Vuex篇
文章分类
前端