vue . extend or vue-class-component

使用TypeScript創(chuàng)建Vue組件時(shí),建議使用兩種格式。

Vue.extend():使用默認(rèn)的Vue構(gòu)造函數(shù)創(chuàng)建“子類”。

此種寫法與 Vue 單文件組件標(biāo)準(zhǔn)形式最為接近,唯一不同僅是組件選項(xiàng)需要被包裹在 Vue.extend() 中。
  • vue-class-component:通常與 vue-property-decorator 一起使用,提供一系列裝飾器,能讓我們書寫類風(fēng)格的 Vue 組件。
  • 兩種形式輸出結(jié)果一致,同是創(chuàng)建一個(gè) Vue 子類,但在書寫組件選項(xiàng)如 props,mixin 時(shí),有些不同。特別是當(dāng)你使用 Vue.extend() 時(shí),為了讓 TypeScript 正確推斷類型,你將不得不做一些額外的處理。接下來(lái),我們來(lái)聊一聊它們的細(xì)節(jié)差異。

    Prop

    由于組件實(shí)例的作用域是孤立的,當(dāng)從父組件傳遞數(shù)據(jù)到子組件時(shí),我們通常使用 Prop 選項(xiàng)。同時(shí),為了確保 Prop 的類型安全,我們會(huì)給 Prop 添加指定類型驗(yàn)證,形式如下:

    export default {

    props: {

    someProp: {

    type: Object,

    required: true,

    default: () => ({ message: 'test' })

    }

    }

    }

    我們定義了一個(gè) someProp,它的類型是 Object。

    使用 JavaScript 時(shí),這并沒(méi)有什么不對(duì)的地方,但當(dāng)你使用 TypeScript 時(shí),這有點(diǎn)不足,我們并不能得到有關(guān)于 someProp 更多有用的信息(比如它含有某些屬性),甚至在 TypeScript 看來(lái),這將會(huì)是一個(gè) any 類型:

    這意味著我們可以使用 someProp 上的任意屬性(存在或者是不存在的)都可以通過(guò)編譯。為了防止此種情況的發(fā)生,我們將會(huì)給 Prop 添加類型注釋。

    Vue.extend()

    使用 Vue.extend() 方法添加類型注釋時(shí),需要給 type 斷言:

    import Vue from 'vue'

    interface User {

    name: string,

    age: number

    }

    export default Vue.extend({

    props: {

    testProps: {

    type: Object as () => User

    }

    }

    })

    當(dāng)組件內(nèi)訪問(wèn) testProps 時(shí),便能得到相關(guān)提示:

    然而,你必須以函數(shù)返回值的形式斷言,并不能直接斷言:

    export default Vue.extend({

    props: {

    testProps: {

    type: Object as User

    }

    }

    })

    它會(huì)給出錯(cuò)誤警告,User 接口并沒(méi)有實(shí)現(xiàn)原生 Object 構(gòu)造函數(shù)所執(zhí)行的方法:

    Type 'ObjectConstructor' cannot be converted to type 'User'. Property 'id' is missing in type 'ObjectConstructor'.

    實(shí)際上,我們可從 Prop type declaration :

    export type Prop<T> = { (): T } | { new (...args: any[]): T & object }

    export type PropValidator<T> = PropOptions<T> | Prop<T> | Prop<T>[];

    export interface PropOptions<T=any> {

    type?: Prop<T> | Prop<T>[];

    required?: boolean;

    default?: T | null | undefined | (() => object);

    validator?(value: T): boolean;

    }

    可知 Prop type 可以以兩種不同方式出現(xiàn):

    • 含有一個(gè)調(diào)用簽名的范型 type,該簽名返回 T;
    • 一個(gè)范型構(gòu)造函數(shù)簽名,該函數(shù)創(chuàng)建指定類型 T 對(duì)象 (返回值 T & object 用于降低優(yōu)先級(jí),當(dāng)兩種方式同時(shí)滿足時(shí)取第一種,其次它還可以用于標(biāo)記構(gòu)造函數(shù)不應(yīng)該返回原始類型)。

    當(dāng)我們指定 type 類型為 String/Number/Boolean/Array/Object/Date/Function/Symbol 等原生構(gòu)造函數(shù)時(shí),Prop<T> 會(huì)返回它們各自簽名的返回值。

    當(dāng) type 類型為 String 構(gòu)造函數(shù)時(shí),它的調(diào)用簽名返回為 string:

    // lib.e

    interface StringConstructor {

    new(value?: any): String;

    (value?: any): string;

    readonly prototype: String;

    fromCharCode(...codes: number[]): string;

    }

    而這也是上文中,當(dāng)指定 type 類型為 Object 構(gòu)造函數(shù)時(shí),經(jīng)過(guò) Vue 的聲明文件處理,TypeScript 推斷出為 any 類型的原因:

    interface ObjectConstructor {

    new(value?: any): Object;

    (): any;

    (value: any): any;

    // 其它屬性 ....

    }

    類似的,當(dāng)我們使用關(guān)鍵字 as 斷言 Object 為 () => User 時(shí),它能推斷出為 User 。

    從 type 第二部分可知,除傳入原生構(gòu)造函數(shù)外,我們還可傳入自定義類:

    此外,這里有個(gè) PR 暴露一個(gè)更直觀的類型( Vue 2.6 版本才可以用):

    props: {

    testProp: Object as PropTypes<{ test: boolean }>

    }

    vue-class-component

    得益于 vue-propperty-decorator Prop 修飾器,當(dāng)給 Prop 增加類型推斷時(shí),這些將變得簡(jiǎn)單:

    import { Component, Vue, Prop } from 'vue-property-decorator'

    @Component

    export default class Test extends Vue {

    @Prop({ type: Object })

    private test: { value: string }

    }

    當(dāng)我們?cè)诮M件內(nèi)訪問(wèn) test 時(shí),便能獲取它正確的類型信息。

    mixins

    mixins 是一種分發(fā) Vue 組件中可復(fù)用功能的一種方式。當(dāng)在 TypeScript 中使用它時(shí),我們希望得到有關(guān)于 mixins 的類型信息。

    當(dāng)你使用 Vue.extends() 時(shí),這有點(diǎn)困難,它并不能推斷出 mixins 里的類型:

    // Exam

    export default Vue.extend({

    data () {

    return {

    testValue: 'test'

    }

    }

    })

    // o

    export default Vue.extend({

    mixins: [ExampleMixin],

    created () {

    // error, testValue 不存在!

    }

    })

    我們需要稍作修改:

    // o

    export default Exam({

    mixins: [ExampleMixin],

    created () {

    // 編譯通過(guò)

    }

    })

    但這會(huì)存在一個(gè)問(wèn)題,當(dāng)使用多個(gè) mixins 且推斷出類型時(shí),這將無(wú)法工作。而在這個(gè) Issuse 中官方也明確表示,這無(wú)法被修改。

    使用 vue-class-component 這會(huì)方便很多:

    // Exam

    import Vue from 'vue'

    import Component from 'vue-class-component'

    @Component

    export class ExampleMixin extends Vue {

    public testValue = 'test'

    }

    // o

    import Component, { mixins } from 'vue-class-component'

    import ExampleMixin from 'Exam'

    @Component({

    components: {

    ExampleMixin

    }

    })

    export class MyComp extends mixins(ExampleMixin) {

    created () {

    con() // 編譯通過(guò)

    }

    }

    也支持可以傳入多個(gè) mixins。

    一些其它

    做為 Vue 中最正統(tǒng)的方法(與標(biāo)準(zhǔn)形式最為接近),Vue.extends() 有著自己的優(yōu)勢(shì),在 VScode Vetur 插件輔助下,它能正確提示子組件上的 Props:

    而類做為 TypeScript 特殊的存在(它既可以作為類型,也可以作為值),當(dāng)我們使用 vue-class-component 并通過(guò) $refs 綁定為子類組件時(shí),便能獲取子組件上暴露的類型信息:

    導(dǎo)入 .vue 時(shí),為什么會(huì)報(bào)錯(cuò)?

    當(dāng)你在 Vue 中使用 TypeScript 時(shí),所遇到的第一個(gè)問(wèn)題即是在 ts 文件中找不到 .vue 文件,即使你所寫的路徑并沒(méi)有問(wèn)題:

    在 TypeScript 中,它僅識(shí)別 js/ts/jsx/tsx 文件,為了讓它識(shí)別 .vue 文件,我們需要顯式告訴 TypeScript,vue 文件存在,并且指定導(dǎo)出 VueConstructor:

    declare module '*.vue' {

    import Vue from 'vue'

    export default Vue

    }

    但是,這引起了另一個(gè)問(wèn)題,當(dāng)我們導(dǎo)入一個(gè)并不存在的 .vue 文件時(shí),也能通過(guò)編譯:

    是的,這在情理之中。

    當(dāng)我嘗試在 .vue 文件中導(dǎo)入已存在或者不存在的 .vue 文件時(shí),卻得到不同的結(jié)果:

    文件不存在時(shí):

    文件存在時(shí):

    文件不存在時(shí),引用 Vue 的聲明文件。文件存在時(shí),引用正確的文件定義。

    這讓人很困惑,而這些都是 Vetur 的功勞。

    1.《詳解在Vue中使用TypeScript的一些思考(實(shí)踐)》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識(shí),僅代表作者本人觀點(diǎn),與本網(wǎng)站無(wú)關(guān),侵刪請(qǐng)聯(lián)系頁(yè)腳下方聯(lián)系方式。

    2.《詳解在Vue中使用TypeScript的一些思考(實(shí)踐)》僅供讀者參考,本網(wǎng)站未對(duì)該內(nèi)容進(jìn)行證實(shí),對(duì)其原創(chuàng)性、真實(shí)性、完整性、及時(shí)性不作任何保證。

    3.文章轉(zhuǎn)載時(shí)請(qǐng)保留本站內(nèi)容來(lái)源地址,http://f99ss.com/gl/2106805.html