记一个Vue+TypeScript项目配置实例

2,750 阅读4分钟

最近想学习一下TypeScript语法,但是只是看官方文档又有些乏味,还是通过项目在实践中学习比较有趣,所以在这里记录一下我的学习历程,与Vue项目结合开发。(官方文档 请戳 >>

项目搭建

通过脚手架搭建

1. 通过Vue CLI 3 创建vue项目

vue create vue-typescript

// 在此选择typescript支持

? Check the features needed for your project: () Babel () TypeScript ( ) Progressive Web App (PWA) Support () Router () Vuex >() CSS Pre-processors () Linter / Formatter ( ) Unit Testing ( ) E2E Testing

// 引入 vue-class-component 插件 // 以下大概是我的选择 ? Use class-style component syntax? (Y/n) y ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass) ? Pick a linter / formatter config: Prettier ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? (y/N) n

2. 启动项目

yarn run serve

能跑起来吗,能跑就是好项目😂

3.项目配置

此时其实脚手架已经帮我们配置好了大多数的配置,但还是需要熟悉一下配置。

tsconfig.json

在项目根目录下创建tsconfig.json

{
  // 编译选项
  "compilerOptions": {
    // 输出目录
    "outDir": "./output",
    // 是否包含可以用于 debug 的 sourceMap
    "sourceMap": true,
    // 以严格模式解析
    "strict": true,
    // 采用的模块系统
    "module": "esnext",
    // 如何处理模块
    "moduleResolution": "node",
    // 编译输出目标 ES 版本
    "target": "es5",
    // 允许从没有设置默认导出的模块中默认导入
    "allowSyntheticDefaultImports": true,
    // 将每个文件作为单独的模块
    "isolatedModules": false,
    // 启用装饰器
    "experimentalDecorators": true,
    // 启用设计类型元数据(用于反射)
    "emitDecoratorMetadata": true,
    // 在表达式和声明上有隐含的any类型时报错
    "noImplicitAny": false,
    // 不是函数的所有返回路径都有返回值时报错。
    "noImplicitReturns": true,
    // 从 tslib 导入外部帮助库: 比如__extends,__rest等
    "importHelpers": true,
    // 编译过程中打印文件名
    "listFiles": true,
    // 移除注释
    "removeComments": true,
    "suppressImplicitAnyIndexErrors": true,
    // 允许编译javascript文件
    "allowJs": true,
    // 解析非相对模块名的基准目录
    "baseUrl": "./",
    // 指定特殊模块的路径
    "paths": {
      "jquery": [
        "node_modules/jquery/dist/jquery"
      ]
    },
    // 编译过程中需要引入的库文件的列表
    "lib": [
      "dom",
      "es2015",
      "es2015.promise"
    ]
  }
}
tslint.json

在根路径下创建tslint.json文件,就是 引入 tsstandard 规范。

如果已经引入了eslint的配置文件,这一步其实也可以不做。

{
  "extends": "tslint-config-standard",
  "globals": {
    "require": true
  }
} 

附上一个脚手架自动生成的eslint的默认配置 eslintrc.js

module.exports = {
  root: true,
  env: {
    node: true
  },
  extends: [
    "plugin:vue/essential",
    "eslint:recommended",
    "@vue/typescript/recommended",
    "@vue/prettier",
    "@vue/prettier/@typescript-eslint"
  ],
  parserOptions: {
    ecmaVersion: 2020
  },
  rules: {
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
  }
};

4.支持ES6 / ES7

tsconfig.json中,添加对es6 / es7的支持,更多的配置请见tsconfig - 编译选项

"lib": [
    "dom",
    "es5",
    "es6",
    "es7",
    "es2015.promise"
]

5.配置Vuex

首先当然是先安装依赖啦。

yarn add vuex vuex-class
  • vuex:在 vue 中集中管理应用状态
  • vuex-class :在 vue-class-component 写法中 绑定 vuex

引用了vuex-class之后还是和原来的写法有点儿区别的。

vuex store 中该怎么写,还怎么写,引用的时候如下:

import { Component, Prop, Vue } from "vue-property-decorator";
import { State, Getter, Action, Mutation, namespace } from "vuex-class";
// 声明使用的是哪个模块
const commonModule = namespace("common");
@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;
  // 获取vuex中的数据
  @commonModule.State("token") token!: string;
  @commonModule.Getter("getToken") getToken!: string;
  @commonModule.Action("setToken") actionSetToken: (arg0: string) => void;
  @commonModule.Mutation("setToken") mutationSetToken: unknown;
  // @State token
  // @Getter("token") getterToken;
  // @Action("token") actionToken;
  // @Mutation("token") mutationToken;
  created() {
    this.actionSetToken("123");
    alert(this.token);
  }
}

6.支持 vue mixin 函数

在src下新建mixins目录,根据业务复用模块划分结构。

在使用Vue进行开发时我们经常要用到混合,结合TypeScript之后一般有两种mixins的方法。

一种是vue-property-decorator提供的

// 定义 routerMixins.ts文件
// mixin router 公共方法
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class myMixins extends Vue {
  /**
   * @author: WangXinYu
   * @describe: 浏览器后退事件
   * @param: {}
   * @return:
   **/
  goBack() {
    this.$router.go(-1)
  }
  /**
   * @author: WangXinYu
   * @describe: test
   * @param: {}
   * @return:
   **/
  routerTest() {
    console.log('are you ok?')
  }
}

// 引入 mixin
import { Component, Prop, Vue, Mixins } from 'vue-property-decorator'
import routerMixins from '@/mixins/router'
@Component
export default class HelloWorld extends Mixins(routerMixins) {
  created() {
    this.routerTest()
  }
}

第二种是在@Component中混入。

// mixins.ts
import { Vue, Component } from 'vue-property-decorator';


declare module 'vue/types/vue' {
    interface Vue {
        value: string;
    }
}

@Component
export default class myMixins extends Vue {
    value: string = 'Hello'
}
// 混入
import { Vue, Component, Prop } from 'vue-property-decorator';
import myMixins from '@/mixins/myMixins';

@Component({
    mixins: [myMixins]
})
export default class myComponent extends Vue{
    created(){
        console.log(this.value) // => Hello
    }
}

7.配置axios以及模块化管理api

首先,肯定是先安装依赖。

yarn add axios

然后,我在 @/plugins目录下新建了axios/index.ts文件

代码如下,仅供参考。

import axios from 'axios'
// import qs from 'qs'
import store from '../../store'

const urlSearchParams = function(data: { [x: string]: string }) {
  const params = new URLSearchParams()
  // var params = new FormData()
  for (const i in data) {
    params.append(i, data[i])
  }
  return params
}
const formDataParams = function(data: { [x: string]: string | Blob }) {
  const params = new FormData()
  for (const i in data) {
    params.append(i, data[i])
  }
  return params
}
/** **** 创建axios实例 ******/
const service = axios.create({
  baseURL: process.env.VUE_APP_URL, // api的base_url
  timeout: 5000 // 请求超时时间
})

/** **** request拦截器==>对请求参数做处理 ******/
service.interceptors.request.use(
  config => {
    // @ts-ignore
    config.headers['Authorization'] = store.state.openid
    // config.headers['Authorization'] = ''
    // 根据类型判断 去将数据转化成不同格式 并设置Content-Type
    // @ts-ignore
    const reqMethod = config.method.toLowerCase()
    switch (reqMethod) {
      case 'post':
        config.headers['Content-Type'] = 'application/json;charset=UTF-8'
        // config.data = JSON.stringify(config.data)
        // config.data = config.data
        break
      case 'postparam':
        // 对应后台 @requestParam 注解
        config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
        config.data = urlSearchParams(config.data)
        config.method = 'post'
        break
      case 'put':
        config.headers['Content-Type'] = 'application/json;charset=UTF-8'
        config.data = JSON.stringify(config.data)
        break
      case 'putparam':
        // 对应后台 @requestParam 注解
        config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
        config.data = urlSearchParams(config.data)
        config.method = 'put'
        break
      case 'upload':
        config.headers['Content-Type'] = 'multipart/form-data'
        config.data = formDataParams(config.data)
        config.method = 'post'
        break
      case 'delete':
        config.headers['Content-Type'] = 'application/json;charset=UTF-8'
        config.params = { ...config.params }
        config.method = 'delete'
        break
      default:
        config.params = { ...config.params }
        config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
        break
    }
    return config
  },
  error => {
    // 请求错误处理
    Promise.reject(error)
  }
)

/** **** respone拦截器==>对响应做处理 ******/
service.interceptors.response.use(
  response => {
    // 成功请求到数据
    // 这里根据后端提供的数据进行对应的处理
    if (response.data.code === 200) {
      return response.data.data
    } else if (!response.data.code) {
      return response.data
    }
  },
  error => {
    // 响应错误处理
    // console.log('error------------------------')
    // console.log(JSON.parse(JSON.stringify(error)))
    // let text = JSON.parse(JSON.stringify(error)).response.status === 404
    //   ? '404'
    //   : '网络异常,请重试'
    // app.$vux.toast.show({
    //   type: 'warn',
    // text: '网络异常,请重试'
    // })

    return Promise.reject(error)
  }
)

export default service

然后就是模块化管理api了,在 src目录下新建api文件夹。

// index.ts文件
/*
 * @Descripttion: api 主输出文件
 * @Author: jooey wong
 */
import ApiCommon from './ApiCommon'
export {
    ApiCommon
}
// api 模块
/*
 * @Descripttion: api ApiCommon 模块
 * @version:
 * @Author: jooey wong
 * @LastEditors: jooey wong
 */

import service from '@/plugins/axios/request'

export default {
  /**
   * @author: WangXinYu
   * @describe: 测试返回值
   * @param: {}
   * @return: promise
   **/
  getCommonData() {
    return service({
      url: '/getTest',
      method: 'get'
    })
  }
}

在这里不建议将api模块直接挂载到Vue的原型链上,如果模块和方法过多时,很影响性能和编译速度的。

(未完待续,根据项目进度,随缘更新...😏)

本文使用 mdnice 排版