在Vue 中使用Typescript

Vue 中使用 typescript

什麼是typescript

typescript 爲 javaScript的超集,這意味着它支持全部都JavaScript都語法。它很像JavaScript都強類型版本,除此以外,它還有一些擴展的語法,如interface/module等。
typescript 在編譯期會去掉類型和特有語法,生成純粹的JavaScript。javascript

Typescript 5年內的熱度隨時間變化的趨勢,總體呈現一個上升的趨勢。也說明ts愈來愈️受你們的關注了。
google 趨勢vue

安裝typescript

npm install -g typescript
tsc greeter.ts

舉個栗子

左右對比能夠看出typescript 在編譯期會去掉類型和特有語法,生成純粹的JavaScript。
greeter.tsjava

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person: Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "Jane", lastName: "User" };

greeter.jswebpack

function greeter(person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}
var user = { firstName: "Jane", lastName: "User" };

爲何須要使用它?

優點:web

  1. 靜態類型檢查
  2. IDE 智能提示
  3. 代碼重構
  4. 可讀性

1. 靜態類型檢查

靜態類型檢查首要優勢就是能儘早的發現邏輯錯誤,而不是上線以後才發現。
1.1 類型分析
傳參過程字段錯誤,或類型錯誤使用。(進行參數標註後,在編碼過程當中便可檢查出錯誤。)
1.2 類型推斷:函數的返回值可經過ts類型推斷得出.這一步驟是 在編譯時進行
在編譯時進行類型分析vue-router

example:
eg1: 我在使用ts寫vue-router 的 動態路徑參數時就發現了一個問題, 動態路徑參數 以冒號開頭 path: '/user/:id',咱們會誤認爲id爲一個number,若是使用ts你將獲得提示 咱們應該傳入一個string類型的id. 傳入一個number類型的id可能並不會出錯,js會對它進行隱式類型轉換,可是傳入一個string會使它更安全和規範.typescript

eg2: 我的使用後的效果shell

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person: Person): string {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: 1223, lastname: "User" };

greeter(user);

greeter error

2.智能補全

在編寫代碼時ide就會提示函數簽名.npm

interface Person {
    firstName: string;
    lastName: string;
}
/**
 * 問候語句
 * @param {Person} person
 * @returns {string}
 */
function greeter(person: Person): string {
    return "Hello, " + person.firstName + " " + person.lastName;
}

/**
 * hello word!
 *
 * @param {string} word
 * @returns {string}
 */
function Hello(word: string): string {
    return "hello," + word;
}

export { greeter, Hello };

直接將這個ts文件引入到其餘ts文件中,不只補全了全部的參數類型,還告訴你須要填入一個參數,而且你只有填入一個Person類型的對象纔不會報錯。(智能補全和參數校驗)
greeter 函數編程

3.在重構上

動態一時爽,重構火葬場.
typescript 在重構上的優點,咱們主要從三方面說明。

  1. 重命名符號,可將一切引用的地方都進行修改。
    在vs code 中若是咱們想修改函數、變量或者類的名稱,咱們可使用重命名符號的功能,在當前項目中正確的修改全部的引用.這個既能夠在ts中使用,也能夠在js中使用,而它的底層實現都是依靠ts 的語法分析器實現的。
  2. 自動更新引用路徑(vs code)。
    在重構的過程當中,咱們可能須要移動文件的路徑,這每每會致使其餘地方的import失效,這時候vs code提供了自動更新引用路徑的功能。它的底層實現也是依靠ts 的語法分析器實現的。
  3. 校驗函數簽名。
    有時候咱們會重構類或函數的簽名,若是有引用到的地方忘記修改,除了運行時候能發現,其餘時候每每難以察覺,且 ESLint 也只能是排查簡單的問題,因此出了BUG會很是麻煩。 而 TypeScript 不同,在編碼時就能及時的發現哪裏錯了,哪裏應該改動但沒有修改。

    [函數簽名 MDN][5]

4. 可讀性

可讀性上,TypeScript 明顯佔優,查看開源代碼時,若是註釋不是很完善,每每會看的雲裏霧裏,而 TypeScript 在同等條件下,至少有個類型,能讓本身更容易明白代碼的參數、返回值和意圖。

TS+Vue初探

配置

在正式開發以前,咱們須要瞭解一些基本的配置。
1.tsconfig.json 是 ts 項目的編譯選項配置文件. 在 ts 項目中若是你不添加這份文件,ts 會使用默認的配置. 掃描二維碼獲取配置項目。
tsconfig.json 配置

  1. ts-loader:Webpack 的TypeScript 加載器,就是爲了讓 webpack 編譯 .ts .tsx文件。
  2. TSLint:.ts .tsx文件的代碼風格檢查工具。(做用相似於ESLint)
  3. vue-shim.d.ts:因爲 TypeScript 默認並不支持 *.vue 後綴的文件,因此在 vue 項目中引入的時候須要建立一個 vue-shim.d.ts 文件,放在項目根目錄下,例如 src/vue-shim.d.ts。
    vue-shim.ts

在Vue裏面寫TS的方式

圖中內容應寫在script中的lang="ts" 。
上圖:使用 Vue.extend的基礎用法。
vue.extend
下圖:基於類的Vue組件
vue 組件
經過對比咱們發現,上面的編碼方式更接近咱們平時JS的寫法,可是咱們並不能感覺到ts的類型檢查和類型推斷。
下面張圖的寫法更有助於咱們獲得ts的類型檢查。這須要咱們引入 vue-class-component ,雖然template中仍是不能獲得補全,可是script 中的內容獲得了更好的補全。 下面咱們瞭解一下vue-class-component的做用。

vue-class-component & vue-property-decorator

vue-class-component 強化 Vue 組件,使用裝飾器語法使 Vue 組件更好的跟TS結合使用。
vue-property-decorator在 vue-class-component 的基礎上增長了更多與 Vue 相關的裝飾器,使Vue組件更好的跟TS結合使用。

這二者都是離不開裝飾器的,(decorator)裝飾器已在ES提案中。Decorator是裝飾器模式的實踐。裝飾器模式呢,它是繼承關係的一個替代方案。動態地給對象添加額外的職責。在不改變接口的前提下,加強類的性能。下面咱們以 鋼鐵俠 爲例講解如何使用 ES7 的 decorator。

以鋼鐵俠爲例,鋼鐵俠本質是一我的,只是「裝飾」了不少武器方纔變得那麼 NB,不過再怎麼裝飾他仍是一我的,它本質是沒有被改變的。因此,裝飾器沒有改變其繼承關係,但也一樣可以爲它添加不少厲害的技能。簡單的說,裝飾器是在不修改一個類的繼承關係的前提下,爲一個類修改或添加成員。
superman
裝飾器主要接收的三個參數:
target 要在其上定義屬性的對象。
key 要定義或修改的屬性的名稱。
descriptor 將被定義或修改的屬性描述符。
下面咱們經過代碼中咱們爲一我的添加了飛行的功能:
decoractor

typescript VS JavaScript

瞭解了上面的基礎知識,如今我將同一段代碼分別使用js 和 ts來書寫,如今咱們來對比他們之間的差異。
js vs ts

  1. Props (Properties)
    使用js,咱們有不少中方式來定義組件的 Props,可是大多都摻雜了 Vue 的私有特徵,與 ES 格格不入,例如左邊的代碼,明明咱們是把這個對象的 prop 屬性定義成爲了一個包含兩個 string 元素的對象,可是咱們卻能夠直接經過這個對象來訪問 "name" 字段,這很明顯是不符合 ES 語義的。
    再來看看右邊的 TS 選手,經過 Prop 裝飾器把指定的字段標記爲了 Prop,既保留了 ES 語法的語義,並且還能與 Vue 完美的配合,更棒的是,咱們能夠在編碼的過程當中享受 TS 對 Prop 字段的靜態類型檢查。
  2. Method 和 data
    再來看看 Method,JS 中定義 method 仍是有咱們上面提到的那個不符合 ES 語義的毛病。而在 TS 中,method 不須要額外的裝飾器——實例方法就會自動成爲 Vue 組件的 method。相似的還有 data ,使用 TS 的語法,實例字段便可自動成爲 Vue 組件的 data。
  3. Computed
    在傳統的使用 JS 編寫的 Vue 代碼中,若是要定義計算屬性,咱們須要在 computed 屬性中定義相應的函數。而這在 ES 中其實早就已經有了對應語義的語法——getter,因此在使用了 vue-class-component 的 vue 組件中,咱們能夠直接使用 getter 來定義計算屬性,不論是在語法上仍是在語義上,相比普通的 JS 都略勝一籌

總結:咱們使用vue-class-component讓vue組件的定義更加符合ES語義,使得TS可以更好的進行語法分析,並基於此進行類型檢查。

業務場景中使用TS + Vue

1. 在Vue項目中定義data和props

vue-property-decorator 這樣的寫法,讓我產生了兩個疑惑。
1.1 爲何使用 的寫法
@Prop(Number!:) propA!: number
而不是
@Prop(Number) propA: number
1.2 爲何Prop須要先後寫兩次類型?

自我自答環節:
答1.1:由於咱們定一個Phone這個類,沒有對phone、condition這些字段經過constructor進行初始化,因此須要在屬性上使用 顯式賦值斷言來幫助類型系統識別類型,這樣可以讓屬性會被間接地初始化。
答1.2:前面括號裏面的類型標註是Vue提供的類型檢查。冒號後面的是爲TS提供的類型標註。

2. 編寫一個函數

這裏咱們將使用到js的接口,它常常用於定義複雜的參數類型和返回值類型。
interface
上面的例子,咱們須要爲opts這個參數定義類型,方便後面編碼時的使用,這時咱們就須要編寫一個接口來對它進行類型定義。這個接口裏麪包括三個必須屬性和一個可選屬性。必須屬性在對象初始化的時候必須賦值,可是有時候某個對象中的屬性能夠被忽略的,不必定會被須要。咱們能夠將它設置爲可選項。

隨着業務的發展,一些參數的字段會變的愈來愈多,愈來愈複雜。可能你想有沒有什麼一勞永逸的方法,讓接口更加簡單,甚至讓它自動的適應業務的變化。因而我想出了這樣的代碼:
type interface

上面的代碼: 定義了一個鍵爲string,他的值爲number或string類型的接口,而且該接口的全部字段都是可選的,你甚至能夠傳入一個空對象。因此咱們可使用上面的接口能夠代替下面的接口,可是反之不行.

然而咱們不該該去繞開這些檢查,由於這樣ts就不會爲你檢查使用接口的對象應該存在那些屬性或者方法了。使用ts的意義就被大大減弱了。

3. 編寫第三方依賴

在平常的開發過程當中,咱們常常會遇到將第三方依賴引入到 TS 項目中沒有類型檢查的問題,這每每是由於這些項目沒有提供類型定義文件。這個時候,咱們能夠手動爲這些第三方庫編寫類型定義文件,類型定義文件在編譯後會被徹底忽略,因此並不會對現有代碼產生影響。以上面這個較爲複雜的函數爲例,它的做用是將傳入的全部的參數的全部字段合併到一個新的對象中並返回,儘管他的功能比較簡單,可是爲它編寫類型定義仍是須要一些 TS 的技巧。
assgin1. 外部模塊聲明: 首先咱們須要建立一個拓展名爲 .d.ts 的文件,並在其中聲明一個模塊,聲明的模塊名稱須要跟咱們在其餘文件中引入的路徑相同。
外部模塊聲明 2. 類型參數——泛型:首先讓咱們考慮最簡單的狀況,當傳入一個參數的時候,extend 函數應該返回與參數類型相同的對象,可是咱們在編寫函數的時候並不知道用戶會傳入何種類型的參數,因此咱們能夠定義一個類型參數 T1,這時,extend 就被稱爲泛型函數,T1 也被稱作泛型參數。在上面的例子中,extend 函數接受一個類型爲 T1 參數並返回一個類型爲 T1 的值,T1 須要用戶手動傳入,還好 TS 足夠聰明,在絕大多數狀況下,TS 能夠根據參數類型來自動推斷類型參數,免去了咱們輸入類型參數的繁瑣步驟。只接受一個參數的 extend 函數並無很複雜,咱們能夠繼續考慮一些更復雜的狀況。
繁星
此次讓咱們定義一個接受三個參數的 extend 函數,同時它也接受三個泛型參數,而它返回值類型呢,則是 T1, T2, T3 的交叉類型。交叉類型讓咱們能夠把現有的多種類型疊加到一塊兒成爲一種類型,它包含了所需的全部類型的特性,至關於對這些類型的成員求並集。 例如, T1 & T2 & T3 這個類型的對象同時擁有了這三種類型的成員。在這裏,交叉類型就知足了咱們對 extend 函數返回值類型的要求。值得注意的是,實際的 extend 函數能夠接受不定個數的參數,也就是說,咱們爲它編寫的類型定義也須要同時兼容接受不定個數參數的狀況,這就須要 TS 提供的函數重載功能。
泛型3. 重載:在大多數靜態類型編程語言中,編譯器容許存在參數類型、個數不一樣的多個同名函數,這個特性被稱爲函數重載。TS 支持函數重載的特性,因此咱們能夠定義多個接受不一樣數量參數的 extend 方法,在用戶調用時,TS 會自動的在這些同名函數中選擇正確的重載定義。有了函數重載的幫助,咱們能夠在使用 extend 的大多數場景下享受到類型檢查的好處,只有在參數個數超過4個的時候,TS 纔沒法推斷出返回值類型。須要注意的是在 JS 中,運行時並不提供函數重載的能力,咱們沒法定義多個同名函數,即便他們接受的參數數量並不相同,爲了實現函數重載的效果,開發人員須要手動在單個函數中對參數的類型、數量作出判斷。
重載
到這裏咱們的第三方聲明就完成了,即便一個簡單函數的第三方聲明,咱們也運用了不少ts的相關知識。

我的感覺

前面咱們講解了,使用在vue中使用ts能帶給咱們的種種便利,如今就我我的感覺而言,說一下美中不足的地方。 1.即便使用了ts,template 部分仍沒有靜態類型檢查和IDE智能提示,但官方成員表示在之後的 Vue 單文件中會提供這項功能。 2.將  Vue 單文件組件引入 TS  文件中,沒法正確的提示其文件位置。 3.Vue  周邊工具,好比  Vuex,它對ts的支持薄弱,大量的功能難以直接遷移到ts中,而且沒有好的官方支持的方案。 4.毫無疑問,使用 TS 進行開發,相比於 JS ,咱們須要花費更多的時間和精力。

相關文章
相關標籤/搜索