Vue 中使用 typescript什麼是typescript
typescript 為 javaScript的超集,這意味著它支援所有都JavaScript都文法。它很像JavaScript都強型別版本,除此之外,它還有一些擴充的文法,如interface/module等。
typescript 在編譯期會去掉類型和特有文法,產生純粹的JavaScript。
Typescript 5年內的熱度隨時間變化的趨勢,整體呈現一個上升的趨勢。也說明ts越來越️受大家的關注了。
安裝typescript
npm install -g typescripttsc greeter.ts
舉個栗子
左右對比可以看出typescript 在編譯期會去掉類型和特有文法,產生純粹的JavaScript。
greeter.ts
interface Person { firstName: string; lastName: string;}function greeter(person: Person) { return "Hello, " + person.firstName + " " + person.lastName;}let user = { firstName: "Jane", lastName: "User" };
greeter.js
function greeter(person) { return "Hello, " + person.firstName + " " + person.lastName;}var user = { firstName: "Jane", lastName: "User" };
為什麼需要使用它?
優勢:
- 靜態類型檢查
- IDE 智能提示
- 代碼重構
- 可讀性
1. 靜態類型檢查
靜態類型檢查首要優點就是能儘早的發現邏輯錯誤,而不是上線之後才發現。
1.1 類型分析
傳參過程欄位錯誤,或類型錯誤使用。(進行參數標註後,在編碼過程中即可檢查出錯誤。)
1.2 類型推斷:函數的傳回值可通過ts類型推斷得出.這一步驟是 在編譯時間進行
在編譯時間進行類型分析
example:
eg1: 我在使用ts寫vue-router 的 動態路徑參數時就發現了一個問題, 動態路徑參數 以冒號開頭 path: '/user/:id',我們會誤認為id為一個number,如果使用ts你將得到提示 我們應該傳入一個string類型的id. 傳入一個number類型的id可能並不會出錯,js會對它進行隱式類型轉換,但是傳入一個string會使它更安全和規範.
eg2: 個人使用後的效果
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);
2.智能補全
在編寫代碼時ide就會提示函數簽名.
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類型的對象才不會報錯。(智能補全和參數校正)
3.在重構上
動態一時爽,重構火葬場.
typescript 在重構上的優勢,我們主要從三方面說明。
- 重新命名符號,可將一切引用的地方都進行修改。
在vs code 中如果我們想修改函數、變數或者類的名稱,我們可以使用重新命名符號的功能,在當前項目中正確的修改所有的引用.這個既可以在ts中使用,也可以在js中使用,而它的底層實現都是依靠ts 的文法分析器實現的。
- 自動更新引用路徑(vs code)。
在重構的過程中,我們可能需要移動檔案的路徑,這往往會導致其他地方的import失效,這時候vs code提供了自動更新引用路徑的功能。它的底層實現也是依靠ts 的文法分析器實現的。
校正函數簽名。
有時候我們會重構類或函數的簽名,如果有引用到的地方忘記修改,除了運行時候能發現,其他時候往往難以察覺,且 ESLint 也只能是排查簡單的問題,所以出了BUG會非常麻煩。 而 TypeScript 不一樣,在編碼時就能及時的發現哪裡錯了,哪裡應該改動但沒有修改。
[函數簽名 MDN][5]
4. 可讀性
可讀性上,TypeScript 明顯佔優,查看開原始碼時,如果注釋不是很完善,往往會看的雲裡霧裡,而 TypeScript 在同等條件下,至少有個類型,能讓自己更容易明白代碼的參數、傳回值和意圖。
TS+Vue初探配置
在正式開發之前,我們需要瞭解一些基本的配置。
1.tsconfig.json 是 ts 項目的編譯選項設定檔. 在 ts 項目中如果你不添加這份檔案,ts 會使用預設的配置. 掃描二維碼擷取設定項目。
- ts-loader:Webpack 的TypeScript 載入器,就是為了讓 webpack 編譯 .ts .tsx檔案。
- TSLint:.ts .tsx檔案的代碼風格檢查工具。(作用類似於ESLint)
- vue-shim.d.ts:由於 TypeScript 預設並不支援 *.vue 尾碼的檔案,所以在 vue 項目中引入的時候需要建立一個 vue-shim.d.ts 檔案,放在項目根目錄下,例如 src/vue-shim.d.ts。
在Vue裡面寫TS的方式
圖中內容應寫在script中的lang="ts" 。
:使用 Vue.extend的基礎用法。
:基於類的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,不過再怎麼裝飾他還是一個人,它本質是沒有被改變的。所以,裝飾器沒有改變其繼承關係,但也同樣能夠為它添加很多厲害的技能。簡單的說,裝飾器是在不修改一個類的繼承關係的前提下,為一個類修改或新增成員。
裝飾器主要接收的三個參數:
target 要在其上定義屬性的對象。
key 要定義或修改的屬性的名稱。
descriptor 將被定義或修改的屬性描述符。
下面我們通過代碼中我們為一個人添加了飛行的功能:
typescript VS JavaScript
瞭解了上面的基礎知識,現在我將同一段代碼分別使用js 和 ts來書寫,現在我們來對比他們之間的差別。
- Props (Properties)
使用js,我們有很多中方式來定義組件的 Props,但是大多都摻雜了 Vue 的私人特徵,與 ES 格格不入,例如左邊的代碼,明明我們是把這個對象的 prop 屬性定義成為了一個包含兩個 string 元素的對象,但是我們卻可以直接通過這個對象來訪問 "name" 欄位,這很明顯是不符合 ES 語義的。
再來看看右邊的 TS 選手,通過 Prop 裝飾器把指定的欄位標記為了 Prop,既保留了 ES 文法的語義,而且還能與 Vue 完美的配合,更棒的是,我們可以在編碼的過程中享受 TS 對 Prop 欄位的靜態類型檢查。
- Method 和 data
再來看看 Method,JS 中定義 method 還是有我們上面提到的那個不符合 ES 語義的毛病。而在 TS 中,method 不需要額外的裝飾器——執行個體方法就會自動成為 Vue 組件的 method。類似的還有 data ,使用 TS 的文法,執行個體欄位即可自動成為 Vue 組件的 data。
- Computed
在傳統的使用 JS 編寫的 Vue 代碼中,如果要定義計算屬性,我們需要在 computed 屬性中定義相應的函數。而這在 ES 中其實早就已經有了對應語義的文法——getter,所以在使用了 vue-class-component 的 vue 組件中,我們可以直接使用 getter 來定義計算屬性,不管是在文法上還是在語義上,相比普通的 JS 都略勝一籌
總結:我們使用vue-class-component讓vue組件的定義更加符合ES語義,使得TS能夠更好的進行文法分析,並基於此進行類型檢查。
業務情境中使用TS + Vue1. 在Vue項目中定義data和props
這樣的寫法,讓我產生了兩個疑惑。
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的介面,它經常用於定義複雜的參數類型和傳回值類型。
上面的例子,我們需要為opts這個參數定義類型,方便後面編碼時的使用,這時我們就需要編寫一個介面來對它進行類型定義。這個介面裡麵包括三個必須屬性和一個可選屬性。必須屬性在對象初始化的時候必須賦值,但是有時候某個對象中的屬性可以被忽略的,不一定會被需要。我們可以將它設定為可選項。
隨著業務的發展,一些參數的欄位會變的越來越多,越來越複雜。可能你想有沒有什麼一勞永逸的方法,讓介面更加簡單,甚至讓它自動的適應業務的變化。於是我想出了這樣的代碼:
上面的代碼: 定義了一個鍵為string,他的值為number或string類型的介面,並且該介面的所有欄位都是可選的,你甚至可以傳入一個Null 物件。所以我們可以使用上面的介面可以代替下面的介面,但是反之不行.
然而我們不應該去繞開這些檢查,因為這樣ts就不會為你檢查使用介面的對象應該存在那些屬性或者方法了。使用ts的意義就被大大減弱了。
3. 編寫第三方依賴
在日常的開發過程中,我們經常會遇到將第三方依賴引入到 TS 項目中沒有類型檢查的問題,這往往是因為這些項目沒有提供類型定義檔案。這個時候,我們可以手動為這些第三方庫編寫類型定義檔案,類型定義檔案在編譯後會被完全忽略,所以並不會對現有代碼產生影響。以上面這個較為複雜的函數為例,它的作用是將傳入的所有的參數的所有欄位合并到一個新的對象中並返回,儘管他的功能比較簡單,但是為它編寫類型定義還是需要一些 TS 的技巧。
1. 外部模組聲明: 首先我們需要建立一個拓展名為 .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 ,我們需要花費更多的時間和精力。