總所周知,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,學習新技術須要成本和時間,大多數是一些中大型的公司在推崇。總而言之,多學點老是好事,學習都要多看多練,知道得越多思惟就會更開闊,解決問題的思路也就越多。