vue+TypeScript需要注意的点

5,053 阅读2分钟

一.引入TypeScript

// ts-loader typescript 必须安装,其他的相信你以后也会装上的
npm i ts-loader typescript tslint tslint-loader tslint-config-standard --save-dev
  • ts-loader:TypeScript 为 Webpack 提供了 ts-loader,其实就是为了让webpack识别 .ts .tsx文件
  • tslint-loader跟tslint:我想你也会在.ts .tsx文件 约束代码格式(作用等同于eslint)
  • tslint-config-standard:tslint 配置 standard风格的约束

二.修改webpack配置文件(webpack.base.conf.js)

1.入口文件修改为main.ts

entry: {
    app: './src/main.ts'
}

2.扩展名resolve.extensions新增.ts后缀

extensions: ['.js', '.vue', '.json', '.ts']

3.添加解析ts文件的规则

{
      test: /\.ts$/,
      exclude: /node_modules/,
      enforce: 'pre',
      loader: 'tslint-loader',
},
{
      test: /\.tsx?$/,
      loader: 'ts-loader',
      exclude: /node_modules/,
      options: {
         appendTsSuffixTo: [/\.vue$/]
      }
}

三:添加 tsconfig.json

ts-loader 会检索当前目录下的 tsconfig.json 文件,根据里面定义的规则来解析.ts文件(就跟.babelrc的作用一样)

{
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ],
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "allowJs": true,
    "module": "esnext",
    "target": "es5",
    "moduleResolution": "node",
    "isolatedModules": true,
    "lib": [
      "dom",
      "es5",
      "es6",
      "es7",
      "es2015.promise",
      "scripthost"
    ],
    "sourceMap": true,
    "pretty": true,
    "strictFunctionTypes": false,
    "importHelpers": true
  }
}	

四:添加 tslint.json

   {
    "extends": "tslint-config-standard",
    "globals": {
      "require": true
    },
    "rules": {
      "arrow-return-shorthand": true,
      "callable-types": true,
      "class-name": true,
      "comment-format": [
        true,
        "check-space"
      ],
      "curly": true,
      "deprecation": {
        "severity": "warn"
      },
      "eofline": true,
      "forin": true,
      "import-blacklist": [
        true,
        "rxjs",
        "rxjs/Rx"
      ],
      "import-spacing": true,
      "indent": [
        true,
        "spaces"
      ],
      "interface-over-type-literal": true,
      "label-position": true,
      "max-line-length": [
        true,
        140
      ],
      "member-access": false,
      "member-ordering": [
        true,
        {
          "order": [
            "static-field",
            "instance-field",
            "static-method",
            "instance-method"
          ]
        }
      ],
      "no-arg": true,
      "no-bitwise": true,
      "no-console": [
        true,
        "debug",
        "info",
        "time",
        "timeEnd",
        "trace"
      ],
      "no-construct": true,
      "no-debugger": true,
      "no-duplicate-super": true,
      "no-empty": false,
      "no-empty-interface": true,
      "no-eval": true,
      "no-inferrable-types": [
        true,
        "ignore-params"
      ],
      "no-misused-new": true,
      "no-non-null-assertion": true,
      "no-shadowed-variable": true,
      "no-string-literal": false,
      "no-string-throw": true,
      "no-switch-case-fall-through": true,
      "no-trailing-whitespace": true,
      "no-unnecessary-initializer": true,
      "no-unused-expression": true,
      "no-use-before-declare": true,
      "no-var-keyword": true,
      "object-literal-sort-keys": false,
      "space-before-function-paren": ["error", "never"],
      "ter-indent": 4,
      "one-line": [
        true,
        "check-open-brace",
        "check-catch",
        "check-else",
        "check-whitespace"
      ],
      "prefer-const": true,
      "quotemark": [
        true,
        "single"
      ],
      "radix": true,
      "semicolon": [
        true,
        "always"
      ],
      "triple-equals": [
        true,
        "allow-null-check"
      ],
      "typedef-whitespace": [
        true,
        {
          "call-signature": "nospace",
          "index-signature": "nospace",
          "parameter": "nospace",
          "property-declaration": "nospace",
          "variable-declaration": "nospace"
        }
      ],
      "typeof-compare": true,
      "unified-signatures": true,
      "variable-name": false,
      "whitespace": [
        true,
        "check-branch",
        "check-decl",
        "check-operator",
        "check-separator",
        "check-type"
      ]
    }
  }	

五:让 ts 识别 .vue

由于 TypeScript 默认并不支持 *.vue 后缀的文件,所以在 vue 项目中引入的时候需要创建一个 vue-shim.d.ts 文件,放在项目项目对应使用目录下,例如 src/vue-shim.d.ts

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}	  

意思是告诉 TypeScript *.vue 后缀的文件可以交给 vue 模块来处理。

而在代码中导入 *.vue 文件的时候,需要写上 .vue 后缀。原因还是因为 TypeScript 默认只识别 *.ts 文件,不识别 *.vue 文件:

import Component from 'components/component.vue'     

六:改造vue-router

因为 ts 无法识别 require,所以按需加载路由的时候需要安装声明文件npm i @types/webpack-env -save-dev;然后才可以用const rank = r => require.ensure([], () => r(require('../pages/rank/rank')), 'rank'); 这样的写法;

七:开始修改App.vue文件

  • 在script 标签上加上 lang="ts", 意思是让webpack将这段代码识别为typescript 而非javascript
  • 修改vue组件的构造方式( 跟react组件写法有点类似, 详见官方 ), 如下图
  • 用vue-property-decorator语法改造之前代码
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({})
export default class App extends Vue {
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

</style>

八:改造 .vue 文件

npm i vue-class-component vue-property-decorator --save

  • vue-class-component:强化 Vue 组件,使用 TypeScript/装饰器 增强 Vue 组件
  • vue-property-decorator:在 vue-class-component 上增强更多的结合 Vue 特性的装饰器

1.vue-class-component

vue-class-component 对 Vue 组件进行了一层封装,让 Vue 组件语法在结合了 TypeScript 语法之后更加扁平化:

<template>
  <div>
    <input v-model="msg">
    <p>msg: {{ msg }}</p>
    <p>computed msg: {{ computedMsg }}</p>
    <button @click="greet">Greet</button>
  </div>
</template>

<script lang="ts">
  import Vue from 'vue'
  import Component from 'vue-class-component'

  @Component
  export default class App extends Vue {
    // 初始化数据
    msg = 123

    // 声明周期钩子
    mounted () {
      this.greet()
    }

    // 计算属性
    get computedMsg () {
      return 'computed ' + this.msg
    }

    // 方法
    greet () {
      alert('greeting: ' + this.msg)
    }
  }
</script>

使用时需要注意的问题

问题1: ts 无法识别$ref

解决办法:

  • 直接在 this.refs.xxx 后面申明类型如:this.refs.lyricsLines as HTMLDivElement;
  • 在export default class xxx extends Vue里面声明全部的$ref 的类型
$refs: {
    audio: HTMLAudioElement,
    lyricsLines: HTMLDivElement
}

问题2: 怎么把mixin引入

@Component({
	/*components: {
    	Swipe
	},*/
    mixins: [mixin]
})

2.vue-property-decorator

vue-property-decorator 是在 vue-class-component 上增强了更多的结合 Vue 特性的装饰器,新增了这 7 个装饰器:

  • @Emit
  • @Inject
  • @Model
  • @Prop
  • @Provide
  • @Watch
  • @Component (从 vue-class-component 继承)

在这里列举几个常用的@Prop/@Watch/@Component

import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'

@Component
export class MyComponent extends Vue {
  
  @Prop()
  propA: number = 1

  @Prop({ default: 'default value' })
  propB: string

  @Prop([String, Boolean])
  propC: string | boolean

  @Prop({ type: null })
  propD: any

  @Watch('child')
  onChildChanged(val: string, oldVal: string) { }
}

上面的代码相当于:

export default {
  props: {
    checked: Boolean,
    propA: Number,
    propB: {
      type: String,
      default: 'default value'
    },
    propC: [String, Boolean],
    propD: { type: null }
  }
  methods: {
    onChildChanged(val, oldVal) { }
  },
  watch: {
    'child': {
      handler: 'onChildChanged',
      immediate: false,
      deep: false
    }
  }
}	

九:注意点

1.如果你引用第三方无类型声明的库,那就需要自己编写x.d.ts文件

2.如果引用 ui 组件的时候,如果控制台出现Property '$xxx' does not exist on type 'App'的话,那么可以在vue-shim.d.ts增加

declare module 'vue/types/vue' {
  interface Vue {
    $xxx: any,
  }
}

其他类似的报错:比如我写了一个自定义的权限指令 在vue文件中直接this.auth会报错Property 'auth' does not exist on type 'orderList'.

解决办法:(this as any).auth('TESTXMC_order-status-index');

3.如何全局使用window的变量??

declare global{
    interface Window {
        DEBUG: string;
        API_HOST: string
    }
}  

(window as any).DEBUG = 'dev';        

4.如何引入jquery?

npm install --save jquery
npm install --save-dev @types/jquery

在index.html里面引入

<script src="node_modules/jquery/dist/jquery.js"></script>