若是想本身體驗從 0 開始配置請參考這個文檔 TypeScript-Vue-Starterhtml
這裏不提初始配置,由於 vue-cli 已經默認配置好了,在根目錄下,有個 tsconfig.json 的文件,在 src 的文件夾下面,有 shims-tsx.d.ts, shims-vue.d.ts 文件vue
shims-vue.d.ts 這個文件,主要用於 TypeScript 識別.vue 文件,Ts 默認並不支持導入 vue 文件,這個文件告訴 ts 導入.vue 文件都按VueConstructor<Vue>
處理,所以導入 vue 文件必須寫.vue 後綴,可是這樣一樣的也會形成,就算你寫的導入的 .vue 文件的路徑就算是錯的,靜態檢測也不會檢測到錯誤,若是你把鼠標放上面你會看到錯誤的路徑就是指向這個文件,由於你定義了這個模塊是全部 .vue 後綴的導入都會指向到這個文件,可是若是你的路徑是對的,ts 能讀出正確的 module。
說那麼多可能沒用,看一下下面兩張圖就懂了git
導入地址是正確的
導入地址是錯誤的github
shims-tsx.d.ts 文件,這個文件主要是方便你使用在 ts 中使用 jsx 語法的,若是不使用 jsx 語法,能夠無視這個,可是強烈建議使用 jsx 語法,畢竟模板是無法得到靜態類型提示的,固然,若是你境界高的話,直接用 vue render function。想要使用 jsx 語法的話,配合 babel-plugin-jsx-v-model,這個插件,體驗更佳,這個插件在 jsx 語法中實現了 v-model。vue-cli
推薦使用 Visual Studio Code 和 Vetur 插件,若是用的是 IDE,推薦使用 WebStormtypescript
Vue.extend()
和 vue-class-component
Vue.extend()
:使用基礎 Vue 構造器,建立一個「子類」。 這種方式最接近 Vue 的單文件組件的寫法,若是一個完善 Vue 項目從 JS 改爲 TS,用這種方法很快,只要加上 lang=ts
和一些必要的變量類型就行了,而後用Vue.extend()
包裹就好。請看下面的例子: TypeScript json
vue-class-component
:一般和vue-property-decorator
一塊兒搭配使用,實際使用只使用vue-property-decorator
就行了,vue-property-decorator
是在vue-class-component
上擴展來的,而且提供了不少修飾器好比 @Prop
和@Watch
等等,使用這個能夠編寫類式組件,可是若是你是完善的項目 JS 改 TS 的話,須要改的地方不少。看一下下面的例子: TypeScript segmentfault
能夠看出變化真的很大數組
組件 propsbabel
Vue.extend()
實現 props 其實和 JavaScript 沒有任何差異,可是若是你須要有 Object 變量類型提示,那就有點不同了vue-class-component
實現 props, 須要從 vue-property-decorator 引入 Prop 這個修飾符,使用起來也很是方便,仍是用上面的例子// JavaScript props: ['isVisible', 'title', 'item', 'count', 'items'] // 若是加入了prop驗證,這樣寫 props: { isVisible: { type: Boolean, required: true }, title: { type: [String, Number] }, item: { type: Object }, items: { type: Array, } count: { count: Number } } // TypeScript /* 這種寫法沒有任何改變,可是這樣沒有任何類型提示,這些變量能被TS識別,可是會所有被識別成any,和沒類型檢查同樣 */ props: ['isVisible', 'title', 'item', 'count', 'items'] /* 當加入prop驗證以後,TS就會提示prop類型了,若是是對象的話,還能有對象的成員提示,寫法和JS寫法差很少,只是對象類型(包括對象,數組和函數)的有點差異,這樣寫的話。*/ // 假設item對象的結構是 interface Item { key: string val: string num: number } props: { isVisible: { type: Boolean, required: true }, title: { type: [String, Number] }, item: { // 注意這裏不是 // Object as Item type: Object as () => Item }, itmes: { // 注意這裏不是 // Array as Array<Item> type: Array as () => Array<Item> } count: { count: Number } } // vue-class-component方式 import { Vue, Component, Prop } from 'vue-property-decorator' // 注意要加非空斷言符 ! 否則會報,固然,你定義成any類型當我沒說 /* [non-null-assertion-operator](https://github.com/Microsoft/TypeScript/wiki /What's-new-in-TypeScript#non-null-assertion-operator) 關於非空斷言能夠參考這個 */ @Prop({ required: true }) isVisible!: boolean @Prop() title!: string | number @Prop() item!: Item @Prop() items!: Array<Item> @Prop() count!: number
組件 data computed methods watch
Vue.extend()
實現 data 其實和 JavaScript 沒有任何差異,computed 的話,也沒什麼大的改變,可是有 this 參與運算的必須標明返回值類型,否則會報錯, methods 的處理方式和 computed 的同樣,有 this 參與運算的必須標明返回值類型,watch 也是同樣vue-class-component
實現 data 的話,直接在類裏面寫變量就好,computed 的話,寫法相似 getter 和 setter,methods 處理方式就是直接在裏面寫方法,watch 須要從 vue-property-decorator 引入 Watch 這個修飾符//一個簡單的例子 // Vue.extend() import Vue from 'vue' export default Vue.extend({ data() { return { count: 1, item: { c: '', n: '' } } }, computed: { // 須要標註有 `this` 參與運算的返回值類型 num(): number { return this.count }, name: { // 須要標註有 `this` 參與運算的返回值類型 get(): string { return this.item.n }, set(val: string) { this.item.n = val } } }, watch: { count(newVal: number, oldVal: number): void { console.log(newVal) }, 'item.n'(newVal: string, oldVal: string): void { console.log(newVal) }, item: { handler(newV, oldVal) { console.log(oldVal) }, deep: true } }, methods: { reset(): void { this.$emit('reset') }, getKey(): string { return this.item.c } } }) // vue-class-component import { Vue, Component, Watch } from 'vue-property-decorator' interface KeyValue { c: string n: string } @Component export default class Test extends Vue { // data count: number = 1 item: KeyValue = { c: '', n: '' } // computed get num(): number { return this.count } get name(): string { return this.item.n } // 注意,這裏不能標返回值類型,就算寫void也不行 set name(val: string) { this.item.n = val } // watch @Watch('count') watchCount(newVal: number, oldVal: number): void { console.log(newVal) } @Watch('item.n') watchName(newVal: string, oldVal: string): void { console.log(newVal) } @Watch('item', { deep: true }) watchItem(newVal: KeyValue, oldVal: KeyValue): void { console.log(newVal) } // methods reset(): void { this.$emit('reset') }, getKey(): string { return this.item.c } }
組件 components
Vue.extend()
components 和 JavaScript 寫法徹底一致vue-class-component
須要把導入的組件寫在修飾器@Components({})裏面// Vue.extend import Vue from 'vue' import MainHeader from './header.vue' import MainContent from './content.vue' export default Vue.extend({ components: { MainHeader, MainContent } }) // vue-class-component import { Vue, Component } from 'vue-property-decorator' import MainHeader from './header.vue' import MainContent from './content.vue' @Component({ components: { MainHeader, MainContent } }) export default class extends Vue {}
組件 mixins
Vue.extend()
並不能徹底實現 mixins 多混入的效果,只能混入一個。不推薦混入用這種方式寫,沒法實現多繼承。若是你非要嘗試這種寫法,能夠看看這個Issue,我沒有嘗試過這種寫法,不過有人寫了個例子,能夠做爲參考,可是我嘗試了沒成功
// ExampleMixin.vue export default Vue.extend({ data () { return { testValue: 'test' } } }) // other.vue export default Vue.extend({ mixins: [ExampleMixin], created () { this.testValue // error, testValue 不存在! } }) 咱們須要稍做修改: // other.vue export default ExampleMixin.extend({ mixins: [ExampleMixin], created () { this.testValue // 編譯經過 } })
vue-class-component
可以實現多混入,寫法相似類繼承
// mixin1.ts import Vue from 'vue' export default Vue.extend({ data () { return { valFromMixin1: 'test' } } }) // 不能是 // 這種寫法會報 Mixin1 is not a constructor function type export default { data () { return { valFromMixin1: 'test' } } } // mixin2.ts import { Component, Vue } from 'vue-property-decorator' @Component export default class Mixin2 extends Vue { methodFromMixin2() {} } // test.ts import Mixin1 from './mixin1' import Mixin2 from './mixin2' import { Component, Mixins } from 'vue-property-decorator' export default class Test extends Mixins(Mixin1, Mixin2) { test() { this.methodFromMixin2() console.log(this.valFromMixin1) } } // 若是隻混入一個的話,能夠這樣寫 export default class Test extends Mixin1 {} export default class Test extends Mixin2 {}
這樣寫不只不會報錯,並且編輯器還有提示
這張圖能夠看出,setWatch 是 BookingWatcher 裏面的方法,實際上,Test 這個類並無自身的屬性,都是從 Vue,BookingWatcher 還有 TestMixins 繼承過來的
這個只能用 Vue.extends(),Vue-class-component 無能爲力,能夠看看這個 Issue
這裏提供一個函數式組件的例子
在 Vue 中使用 TypeScript 的一些思考(實踐)
vue 官網 TypeScript 支持
$refs 報錯
$refs 報錯這個問題相信基本都遇到,除非你真沒用到這個,如圖:
報錯信息是
Property 'blur' does not exist on type 'Vue | Element | Vue[] | Element[]'.
Property 'blur' does not exist on type 'Vue'.
解決方案:把上圖報錯部分改爲
// 把這個變量改爲定義成HTMLInputElement就好,這裏須要用到類型斷言 test() { let inputBox: HTMLInputElement = this.$refs.inputBox as HTMLInputElement inputBox.blur() }
若是引用的比較多,而且引用裏面有本身的組件的話,能夠這樣寫:
$refs: {}
這樣編輯器還會提示組件裏面有什麼方法,當你打出 this.$refs.header.時候,編輯器有提示 Header 這個組件裏面的屬性和方法