总所周知,Vue新版本3.0 使用 TypeScript 开发,让原本就很火的 TypeScript 受到更多人的关注。虽然 TypeScript 在近几年才火,但其实它诞生于2012年10月,正式版本发布于2013年6月,是由微软编写的自由和开源的编程语言。TypeScript 是 JavaScript 的一个超集,扩展了 JavaScript 的语法,添加了可选的静态类型和基于类的面向对象编程。javascript
JavaScript开发中常常遇到的错误就是变量或属性不存在,然而这些都是低级错误,而静态类型检查刚好能够弥补这个缺点。什么是静态类型?举个栗子:css
//javascript let str = 'hello' str = 100 //ok //typescript let str:string = 'hello' str = 100 //error: Type '100' is not assignable to type 'string'.
能够看到 TypeScript 在声明变量时须要为变量添加类型,若是变量值和类型不一致则会抛出错误。静态类型只在编译时进行检查,并且最终编译出来的代码依然是 JavaScript。即便咱们为 string 类型的变量赋值为其余类型,代码也是能够正常运行的。html
其次,TypeScript 增长了代码的可读性和可维护性,类型定义实际上就是一个很好的文档,好比在调用函数时,经过查看参数和返回值的类型定义,就大概知道这个函数如何使用。vue
安装 typescriptjava
npm install typescript @vue/cli-plugin-typescript -D
在项目的根目录下建立 shims-vue.d.ts、shims-tsx.d.ts、tsconfig.jsonnode
import Vue from 'vue'; declare module '*.vue' { export default Vue; }
import Vue, { VNode } from 'vue'; declare global { namespace JSX { type Element = VNode type ElementClass = Vue interface IntrinsicElements { [elem: string]: any; } } }
{ "compilerOptions": { "target": "es5", "module": "esnext", "strict": true, "jsx": "preserve", "importHelpers": true, "moduleResolution": "node", "esModuleInterop": true, "allowSyntheticDefaultImports": true, "experimentalDecorators":true, "sourceMap": true, "noImplicitThis": false, "baseUrl": ".", "types": [ "webpack-env" ], "paths": { "@/*": [ "src/*" ] }, "lib": [ "esnext", "dom", "dom.iterable", "scripthost" ] }, "include": [ "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "tests/**/*.ts", "tests/**/*.tsx" ], "exclude": [ "node_modules" ] }
今年1月份,TypeScript官方发布博客推荐使用ESLint来代替TSLint。而 ESLint 团队将再也不维护 typescript-eslint-parser
,也不会在 Npm 上发布,任何使用 tyescript-eslint-parser
的用户应该改用 @tyescript-eslint/parser
。webpack
官方的解释:git
咱们注意到TSLint规则的操做方式存在一些影响性能的体系结构问题,ESLint已经拥有了咱们但愿从linter中获得的更高性能的体系结构。此外,不一样的用户社区一般有针对ESLint而不是TSLint构建的lint规则(例如React hook或Vue的规则)。鉴于此,咱们的编辑团队将专一于利用ESLint,而不是复制工做。对于ESLint目前没有覆盖的场景(例如语义linting或程序范围的linting),咱们将致力于将ESLint的TypeScript支持与TSLint等同起来。github
原文web
AlloyTeam 提供了一套全面的EsLint配置规范,适用于 React/Vue/Typescript 项目,而且能够在此基础上自定义规则。
GitHub
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-alloy
配置项的说明查看AlloyTeam ESLint 规则
在项目的根目录中建立.eslintrc.js,而后将如下内容复制到其中:
module.exports = { extends: [ 'alloy', 'alloy/typescript', ], env: { browser: true, node: true, }, rules: { // 自定义规则 'spaced-comment': 'off', '@typescript-eslint/explicit-member-accessibility': 'off', 'grouped-accessor-pairs': 'off', 'no-constructor-return': 'off', 'no-dupe-else-if': 'off', 'no-import-assign': 'off', 'no-setter-return': 'off', 'prefer-regex-literals': 'off' } };
若是想知道配置项更多使用,能够到ESLint官网搜索配置项。
若是使用的是VScode,推荐使用ESLint插件辅助开发。
const path = require('path') module.exports = { ... pages: { index: { entry: path.resolve(__dirname+'/src/main.ts') }, }, ... }
随着TypeScript和ES6里引入了类,在一些场景下咱们须要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为咱们在类的声明及成员上经过元编程语法添加标注提供了一种方式。
Vue 也为咱们提供了类风格组件的 TypeScript 装饰器,使用装饰器前须要在 tsconfig.json 将 experimentalDecorators 设置为 true。
vue-property-decorator
库彻底依赖vue-class-component
,在安装时要一块儿装上
npm install vue-class-component vue-property-decorator -D
只须要修改srcipt内的东西便可,其余不须要改动
<script lang="ts"> import { Component, Vue } from "vue-property-decorator"; import draggable from 'vuedraggable' @Component({ created(){ }, components:{ draggable } }) export default class MyComponent extends Vue { /* data */ private ButtonGrounp:Array<any> = ['edit', 'del'] public dialogFormVisible:boolean = false /*method*/ setDialogFormVisible(){ this.dialogFormVisible = false } addButton(btn:string){ this.ButtonGrounp.push(btn) } /*compute*/ get routeType(){ return this.$route.params.type } } </script>
类成员修饰符,不添加修饰符则默认为public
!: 为属性使用明确的赋值断言修饰符,了解更多看文档
import { Component, Vue, Prop } from "vue-property-decorator"; export default class MyComponent extends Vue { ... @Prop({type: Number,default: 0}) readonly id!: number ... }
等同于
export default { ... props:{ id:{ type: Number, default: 0 } } ... }
import { Component, Vue, Watch } from "vue-property-decorator"; export default class MyComponent extends Vue { ... @Watch('dialogFormVisible') dialogFormVisibleChange(newVal:boolean){ // 一些操做 } ... }
等同于
export default { ... watch:{ dialogFormVisible(newVal){ // 一些操做 } } ... }
// App.vue import {Component, Vue, Provide} from 'vue-property-decorator' @Component export default class App extends Vue { @Provide() app = this } // MyComponent.vue import {Component, Vue, Inject} from 'vue-property-decorator' @Component export default class MyComponent extends Vue { @Inject() readonly app!: Vue }
等同于
// App.vue export default { provide() { return { 'app': this } } } // MyComponent.vue export default { inject: ['app'] }
更多装饰器使用,参考vue-property-decorator文档
目前主流的库文件都是 JavaScript 编写,TypeScript 身为 JavaScript 的超集,为支持这些库的类型定义,提供了类型定义文件(*.d.ts),开发者编写类型定义文件发布到npm上,当使用者须要在 TypeScript 项目中使用该库时,能够另外下载这个包,让JS库可以在 TypeScript 项目中运行。
好比:md5
相信不少人都使用过,这个库能够将字符串转为一串哈希值,这种转化不可逆,经常使用于敏感信息进行哈希再发送到后端进行验证,保证数据安全性。若是咱们想要在 TypeScript 项目中使用,还须要另外下载 @tyeps/md5
,在该文件夹的index.d.ts中能够看到为 md5
定义的类型。
/// <reference types="node" /> declare function md5(message: string | Buffer | Array<number>): string; declare namespace md5 {} export = md5;
TypeScript 在项目编译时会全局自动识别 .d.ts文件,咱们须要作的就是编写 .d.ts,而后 TypeScript 会将这些编写的类型定义注入到全局提供使用。
当咱们在使用this.$route
或一些原型上的方法时,typescript没法进行推断,在编译时会报属性$route
不存在的错误,须要为这些全局的属性或方法添加全局声明
对shims-vue.d.ts作修改,固然你也能够选择自定义*.d.ts来添加声明
import Vue from 'vue'; import VueRouter, { Route } from 'vue-router' declare module '*.vue' { export default Vue; } declare module 'vue/types/vue' { interface Vue { $api: any; $bus: any; $router: VueRouter; $route: Route; } }
当一些类型或接口等须要频繁使用时,咱们能够为项目编写全局类型定义,
根路径下建立@types文件夹,里面存放*.d.ts文件,专门用于管理项目中的类型定义文件。
这里我定义个global.d.ts文件:
//declare 能够建立 *.d.ts 文件中的变量,declare 只能做用域最外层 //变量 declare var num: number; //类型 type StrOrNum = string | number //函数 declare function handler(str: string): void; // 类 declare class User { } //接口 interface OBJ { [propName: string]: any; [propName: number]: any; } interface RES extends OBJ { resultCode: number; data: any; msg?: string; }
改造过程最麻烦的就是语法转换,内容都是一些固定的写法,这些重复且枯燥的工做能够交给机器去作。这里咱们能够借助 transvue2ts 工具提升效率,transvue2ts 会帮咱们把data、prop、watch等语法转换为装饰器语法。
npm i transvue2ts -g
安装完以后,transvue2ts 库的路径会写到系统的 path中,直接打开命令行工具便可使用,命令的第二个参数是文件的完整路径。
执行命令后会在同级目录生成转换好的新文件,例如处理view文件夹下的index.vue,转换后会生成indexTS.vue。
处理单文件组件
transvue2ts D:\typescript-vue-admin-demo\src\pages\index.vue => 输出路径:D:\typescript-vue-admin-demo\src\pages\indexTS.vue
处理文件夹下的全部vue组件文件
transvue2ts D:\typescript-vue-admin-demo\src\pages => 输出路径:D:\typescript-vue-admin-demo\src\pagesTS
不要觉得有工具真就彻底解放双手,工具只是帮咱们转换部分语法。工具未能处理的语法和参数的类型定义,仍是须要咱们去修改的。要注意的是转换后注释会被过滤掉。
该工具做者在掘金对工具的介绍和实现思路
一些三方库会在安装时,包含有类型定义文件,使用时无需本身去定义,能够直接使用官方提供的类型定义。
node_modules中找到对应的包文件夹,类型文件通常都会存放在types文件夹内,其实类型定义文件就像文档同样,这些内容可以清晰的看到所需参数和参数类型。
这里列出一些在 Vue 中使用三方库的例子:
使用类型定义
import { Component, Vue } from "vue-property-decorator"; import { ElLoadingComponent, LoadingServiceOptions } from 'element-ui/types/loading' let loadingMark:ElLoadingComponent; let loadingConfig:LoadingServiceOptions = { lock: true, text: "加载中", spinner: "el-icon-loading", background: "rgba(255, 255, 255, 0.7)" }; @Component export default class MyComponent extends Vue { ... getList() { loadingMark = this.$loading(loadingConfig); this.$api.getList() .then((res:RES) => { loadingMark.close(); }); } ... }
element-ui/types/loading,原文件里还有不少注释,对每一个属性都作出描述
export interface LoadingServiceOptions { target?: HTMLElement | string body?: boolean fullscreen?: boolean lock?: boolean text?: string spinner?: string background?: string customClass?: string } export declare class ElLoadingComponent extends Vue { close (): void } declare module 'vue/types/vue' { interface Vue { $loading (options: LoadingServiceOptions): ElLoadingComponent } }
使用类型定义
import { Component, Vue } from "vue-property-decorator"; import { NavigationGuard } from "vue-router"; @Component export default class MyComponent extends Vue { beforeRouteUpdate:NavigationGuard = function(to, from, next) { next(); } }
在vue-router/types/router.d.ts中,开头就能够看到钩子函数的类型定义。
export type NavigationGuard<V extends Vue = Vue> = ( to: Route, from: Route, next: (to?: RawLocation | false | ((vm: V) => any) | void) => void ) => any
还有前面所使用到的Router
、Route
,全部的方法、属性、参数等都在这里被描述得清清楚楚
export declare class VueRouter { constructor (options?: RouterOptions); app: Vue; mode: RouterMode; currentRoute: Route; beforeEach (guard: NavigationGuard): Function; beforeResolve (guard: NavigationGuard): Function; afterEach (hook: (to: Route, from: Route) => any): Function; push (location: RawLocation, onComplete?: Function, onAbort?: ErrorHandler): void; replace (location: RawLocation, onComplete?: Function, onAbort?: ErrorHandler): void; go (n: number): void; back (): void; forward (): void; getMatchedComponents (to?: RawLocation | Route): Component[]; onReady (cb: Function, errorCb?: ErrorHandler): void; onError (cb: ErrorHandler): void; addRoutes (routes: RouteConfig[]): void; resolve (to: RawLocation, current?: Route, append?: boolean): { location: Location; route: Route; href: string; normalizedTo: Location; resolved: Route; }; static install: PluginFunction<never>; } export interface Route { path: string; name?: string; hash: string; query: Dictionary<string | (string | null)[]>; params: Dictionary<string>; fullPath: string; matched: RouteRecord[]; redirectedFrom?: string; meta?: any; }
当使用的三方库未带有 *.d.ts 声明文件时,在项目编译时会报这样的错误:
Could not find a declaration file for module 'vuedraggable'. 'D:/typescript-vue-admin-demo/node_modules/vuedraggable/dist/vuedraggable.umd.min.js' implicitly has an 'any' type. Try `npm install @types/vuedraggable` if it exists or add a new declaration (.d.ts) file containing `declare module 'vuedraggable';`
大体意思为 vuedraggable 找不到声明文件,能够尝试安装 @types/vuedraggable(若是存在),或者自定义新的声明文件。
按照提示先选择第一种方式,安装 @types/vuedraggable
,而后发现错误 404 not found,说明这个包不存在。感受这个组件还挺多人用的(周下载量18w),没想到社区竟然没有声明文件。
无奈只能选择第二种方式,说实话本身也摸索了有点时间(主要对这方面没作多了解,不太熟悉)
首先在 node_modules/@types 下建立 vuedraggable 文件夹,若是没有 @types 文件夹可自行建立。vuedraggable 文件夹下建立 index.d.ts。编写如下内容:
import Vue from 'vue' declare class Vuedraggable extends Vue{} export = Vuedraggable
从新编译后没有报错,解决问题。
.ts
后缀抽着空闲时间入门一波 TypeScript,尝试把一个后台管理系统接入 TypeScript,毕竟只有实战才能知道有哪些不足,以上记录都是在 Vue 中如何使用 TypeScript,以及遇到的问题。目前工做中还未正式使用到 TypeScript,学习新技术须要成本和时间,大多数是一些中大型的公司在推崇。总而言之,多学点老是好事,学习都要多看多练,知道得越多思惟就会更开阔,解决问题的思路也就越多。