vue ts 设置tslint提示_Typescript 在 Vue 中的实践(包含2.x、3.x)
1 使用 typescript 的優勢
聊到 ts 時有一個不能規避的問題:為什么要使用 ts ,相比 js 有什么優勢嗎?下面我從兩個方面試著回答一下這個問題:
1.1 項目開發時的便利
- 避免低級 bug 產生
相信大家都遇到在編輯器一頓操作,打開瀏覽器頁面空白的尷尬翻車現場,然后一頓 debug 最后發現是把變量名拼錯了, 用上 ts 之后再也不會有這樣的煩惱了,類似的錯誤編輯器立馬就提示你了:
項目變的越來越龐大時,記住一個變量描述的具體是什么也是一件很困難的事,可能需要我們不停的去查找類型的定義,事實上 ts 可以很好的解決這個問題,雖然一開始的類型定義稍顯繁瑣,但是他帶來的類型提示、代碼補全等會讓我們覺得這份工作量是值得的。
1.2 后期項目維護成本
- 類型即注釋
工作中難免會需要我們對某個模塊升級或者修改等,有時候沒有注釋或者注釋不全的話就需要我們去讀代碼了,其實很多時候我們并不關心某個模塊內部的具體實現,我們只想知道他的輸入輸出,這時候 ts 就很適合這個場景了:
type?Pay?=?(orderId:?string)?=>?Promise<-1?|?0>
const?pay:?Pay?=?orderId?=>?{
??//?do?something
}
類似于上面的代碼,從類型定義就可以大致推斷出其目的
- 利于重構
這可能是類型系統一個比較大的優勢了,之前在重構 js 項目時可謂是如履薄冰,生怕修改了某個模塊后搞崩整個項目,有了類型系統就可以安心多了,編輯器哪里飄紅去修改哪個地方就可以了。
2 讓類型流動起來
通常情況下我們會為項目中的接口添加類型定義,這可能也是最繁瑣的地方。假設我們有一個獲取商品信息的接口如下:
//?goodApi.ts
export?type?GoodInfo?=?{
??infoId:?string,
??price:?string,
??title:?string,
??label:?{
????labeltxt:?string,
????labelImg:?string
??}[]
}
const?getGoodInfo?=?():?GoodInfo[]?=>?{
??//?do?something
}
現在有一個方法 processLabel 需要輸入商品的 label 來對 labelImg 做一些處理,如下:
type?LabelMsg?=?{
??labeltxt:?string,
??labelImg:?string
}[]
const?processLabel?=?(labelArr:?LabelMsg)?=>?{
??//?do?something
}
顯然 labelArr 的源自于 GoodInfo 的 label,所以更好的方式是復用 GoodInfo 中的類型:
import?{?GoodInfo?}?from?'./goodApi.ts'
const?processLabel?=?(labelArr:?GoodInfo['label'])?=>?{
??//?do?something
}
當然,我們遇到的場景不可能都如此的簡單,可能有一個復雜點的函數 processLabel 依賴于兩個或多個接口的返回數據:
import?{?GoodInfo?}?from?'./goodApi.ts'
import?{?UserInfo?}?from?'./userApi.ts'
type?Info?=?Pick'label'>?&?Pick'tag?|?avatar'>const?processLabel?=?(info:?Info)?=>?{//?do?something
}總之,我想表達的是應該盡量的復用類型或者使用類型推導,而不是一味的去聲明新的類型,這樣在項目后期維護或者代碼重構時能帶來一些方便。
3 在 Vue@2.x 中使用 ts
好的接下來進入本文的主題,在 Vue 中使用 ts,Vue 中一個常規的組件可能是這樣的:
export?default?{
??template:?'Click!',
??data()?{
????return?{
??????preStr:?'Hello!'
????}
??},
??props:?{
????message:?{
??????type:?String,
??????default:?'world'
????}
??},
??methods:?{
????onClick?()?{
??????window.alert(this.preStr?+?this.message)
????}
??}
}
Vue@2.x 要用上 ts 通常需要借助于 vue-property-decorator[1] 這個庫,我們大致看一下它的使用方式:
import?Vue?from?'vue'
import?{?Component,?Prop?}?from?'vue-property-decorator';
@Component({
??template:?'Click!'
})
export?default?class?Test?extends?Vue?{
??preStr:?string?=?'Hello!'
??@Prop({?type:?String,?default:?'world'?})
??message:?string
??onClick?():?void?{
????window.alert(this.preStr?+?this.message)
??}
}
可以看到需要將對象字面量的寫法轉換為基于類的組件定義方式,這樣很容易就可以對組件的 state 、 prop 、 method 等添加類型,同時其利用裝飾器在運行時導出了標準的 vue 組件,我們來大致看下 Component 裝飾器[2]的內部原理:
export?function?componentFactory?(//?Component即定義的組件類
??Component:?VueClass,//?options為傳給Component裝飾器的初始選項
??options:?ComponentOptions?=?{}):?VueClass<Vue>?{
??options.name?=?options.name?||?(Component?as?any)._componentTag?||?(Component?as?any).name
??const?proto?=?Component.prototype
??Object.getOwnPropertyNames(proto).forEach(function?(key)?{
????//?如果方法名與vue生命周期同名?直接放進options內
????if?($internalHooks.indexOf(key)?>?-1)?{
??????options[key]?=?proto[key]
??????return
????}
????const?descriptor?=?Object.getOwnPropertyDescriptor(proto,?key)
????//?普通方法放進options的methods內
????if?(descriptor.value?!==?void?0)?{
??????if?(typeof?descriptor.value?===?'function')?{
????????(options.methods?||?(options.methods?=?{}))[key]?=?descriptor.value
??????}
????//?get?set函數放進options的計算computed內
????}?else?if?(descriptor.get?||?descriptor.set)?{
??????(options.computed?||?(options.computed?=?{}))[key]?=?{
????????get:?descriptor.get,
????????set:?descriptor.set
??????}
????}
??})
??;(options.mixins?||?(options.mixins?=?[])).push({
????data?(this:?Vue)?{
??????//?類實例上的屬性當做options中data函數的返回值,即state
??????return?collectDataFromConstructor(this,?Component)
????}
??})
??//?Super就是Vue?根據計算得到的options選項extend一個常規的vue組件
??const?Extended?=?Super.extend(options)
??return?Extended
}
上面是 Component 裝飾器的部分源碼,大致就是收集組件類原型上的方法,按照不同的條件分發到 options 的生命周期方法、普通方法和計算屬性上,然后收集類實例上的屬性作為 options 中 data 函數的返回值,最后根據 options 去 extend 一個標準的 vue 組件。
下面再簡單看下 Prop 裝飾器的原理:
export?function?Prop(options:?PropOptions?|?Constructor[]?|?Constructor?=?{})?{
??return?(target:?Vue,?key:?string)?=>?{
????applyMetadata(options,?target,?key)
????createDecorator((componentOptions,?k)?=>?{
??????//?componentOptions即上文中的options對象??k即被修飾的鍵名
??????;(componentOptions.props?||?((componentOptions.props?=?{})?as?any))[
????????k
??????]?=?options
????})(target,?key)
??}
}
Prop 的工作很簡單,將 Prop 修飾的鍵名和傳入的參數組成鍵值對放進 options.props 中,類似的vue-property-decorator[3]還提供了很多別的裝飾器(Model 、Watch 、 Ref 等),覆蓋了常見的使用場景,其基本原理大致都是在運行時修改構造組件的參數 options。
4 在 Vue@3.x 中使用 ts
Vue@3.x 已經發布了正式版,帶來了 Composition Api 和更好的 typescript 支持。對于 Vue@3.x + ts 的項目,只需通過 defineComponent 定義組件即可完美推斷出 component options 中的類型:
import?{?defineComponent?}?from?'vue'
const?Component?=?defineComponent({
??data()?{
????return?{
??????preStr:?'hello!'
????}
??},
??props:?{
????message:?{
??????type:?String,
??????required:?true
????}
??},
??methods:?{
????onClick()?{
??????this.preStr?=?'hi!'?//?ok????preStr被推斷成?string
??????this.preStr?=?123???//?error?number類型不能分配給string類型
??????this.message?=?'zz'?//?error?message是read-only屬性
????}
??}
})
對于 Composition Api 的支持度也很高:
import?{?defineComponent,?ref,?PropType?}?from?'vue';
export?default?defineComponent({
??props:?{
????callback:?{
??????required:?true,
??????type:?Function?as?PropType<(nums:?number)?=>?void>
????},
????message:?{
??????type:?String,
??????required:?true
????}
??},
??setup(props)?{
????const?nums?=?ref(0)
????props.callback(props.message)??//?error?callback的參數應該是number類型
????props.callback(nums.value)?????//?ok
??}
})
本人也是通過 demo 大概嘗試了一下在 Vue@3.x 中使用 ts,可以說是十分順滑,相較于 Vue@2.x 中基于類的組件定義方式,Vue@3.x 不需要我們改變什么代碼習慣就可輕松接入 ts,并且其類型推導也是相當的強大。
最后如果非要找出個不足的話,Vue 的模板寫法還是不能與 ts 兼容,雖說還有 tsx 的備選方案,但是用 tsx 就不太 Vue 了,不過相信后續也會有優秀的工具來彌補這個不足。總而言之,Vue 對 ts 的支持已足夠強大,趕緊用起來吧,真香!
參考資料
[1]vue-property-decorator: https://github.com/kaorun343/vue-property-decorator
[2]Component 裝飾器: https://github.com/vuejs/vue-class-component/blob/master/src/component.ts
[3]vue-property-decorator: https://github.com/kaorun343/vue-property-decorator
???往期相關文章- 從0到1,帶你徹底搞懂 vite 中的 HMR 原理
-?vue3.0新特性盤點
-?快速體驗Vue2和Vue3組件開發的區別-【官宣】Vue 3.0 發布!???交流討論歡迎關注公眾號?「秋風的筆記」,主要記錄日常中覺得有意思的工具以及分享開發實踐,保持深度和專注度。回復"好友"可加微信,秋風的筆記常年陪伴你的左右。總結
以上是生活随笔為你收集整理的vue ts 设置tslint提示_Typescript 在 Vue 中的实践(包含2.x、3.x)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 一年能挣多少钱啊?
- 下一篇: 怎么看b树是几阶_看我在B站上怎么学习的
