1. 程式人生 > >使用 typescript ,提升 vue 專案的開發體驗(1)

使用 typescript ,提升 vue 專案的開發體驗(1)

此文已由作者張漢銳授權網易雲社群釋出。

歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。


前言:對於我們而言,typescript 更像一個工具


官方指南

從 vue2.5 之後,vue 對 ts 有更好的支援。根據官方文件,vue 結合 typescript ,有兩種書寫方式:


Vue.extend

  import Vue from 'vue'

  const Component = Vue.extend({    // type inference enabled
  })


Class-style Vue Components

  import Vue from 'vue'
  import Component from 'vue-class-component'

  // The @Component decorator indicates the class is a Vue component
  @Component({    // All component options are allowed in here
    template: '<button @click="onClick">Click!</button>'
  })
  export default class MyComponent extends Vue {    // Initial data can be declared as instance properties
    message: string = 'Hello!'

    // Component methods can be declared as instance methods
    onClick (): void {      window.alert(this.message)
    }
  }


理想情況下,Vue.extend 的書寫方式,是學習成本最低的。在現有寫法的基礎上,幾乎 0 成本的遷移

// 現在常見的寫法export default {    // your code }


但「理想豐滿,現實骨感」,問題出在:


  • Vue.exend 在和 vuex 和 mixins 結合使用的時候,無法發揮 ts 的作用,vuex 和 mixins 會在專案中大量使用,這個問題不能忽視。


Vue.extend + vuex + mixins 問題的介紹


Vue.extend + vuex 的問題

由於 vuex 使用  mapState, mapActions 等方法的時候,通過字串形式做對映,這個過程中,丟失了型別資訊。下面的 gif 可以看到,整個過程中:

  • 無法做程式碼提示

  • 無法對對應的 actions  和 state 做型別宣告,使得型別檢查生效

  • 無法使用重構

    Alt pic


顯然,如果只有一部分的方法和屬性得到了程式碼提示和型別檢查,就是失去了使用 typescript 意義。

在 Vue.extend + vuex 寫法下,這個問題暫時沒有解決方案。


Vue.extend + mixins 的問題

同樣的問題,在 mixin 中定義的方法,不會被 typescript 識別到,下面 gif 可以看到,不僅僅「程式碼提示」「型別檢查」「程式碼重構」沒有工作,甚至因識別不到 test 而報錯

Alt pic


Alt pic


Class-Style Components


那麼就剩下 Class-Style Components 方案。當然,這個方案需要做額外的工作才能夠讓「vue 全家桶 + ts」良好的工作。

原理:將屬性直接掛載在 class 上,使得 typescript 能夠良好的進行「程式碼提示」和「型別檢查」。然後再通過裝飾器將屬性轉成 vue 上的屬性。


例如 @Prop, @Watch, @Action 等裝飾器,將屬性做相應的轉換成 props,  watch, mapActions 裡面的值,具體後面例子展示。


vue-class-component


這裡庫提供最基礎的 vue 裝飾器:@Component 。其他的 vue 裝飾器庫,都在這個庫的基礎上做擴充套件和修改。看看官網的例子:


import Vue from 'vue'import Component from 'vue-class-component'// @Component 會將 MyComponent 中的屬性,轉換成 vue 對應的屬性@Component({  // Vue 所有的屬性都可以在這裡宣告,一般用到的比較少
  template: '<button @click="onClick">Click!</button>'})
export default class MyComponent extends Vue {  // @Component 將 message 轉成成 data 
  message: string = 'Hello!'

  // @Component 會將這裡的 getter 屬性,轉換成 computed
  get name(){    return 'anders'
  }  // @Component 識別到 created 是宣告週期關鍵字,不做處理
  created(){}  // @Component 識別到 onClick 不是關鍵字,將它轉成 methods  
  onClick (): void {    window.alert(this.message)
  }
}


vue-property-decorator


這個庫提供了:


  • @Emit

  • @Inject

  • @Model

  • @Prop

  • @Provide

  • @Watch


其中常用的: @Prop,@Watch,@Emit。 看例子:


import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'const s = Symbol('baz')

@Component
export class MyComponent extends Vue {

  @Emit()
  addToCount(n: number){ this.count += n }

  @Emit('reset')
  resetCount(){ this.count = 0 }

  @Prop()
  propA: number

  @Prop({ default: 'default value' })
  propB: string

  @Prop([String, Boolean])
  propC: string | boolean

  @Watch('child')
  onChildChanged(val: string, oldVal: string) { }

  @Watch('person', { immediate: true, deep: true })
  onPersonChanged(val: Person, oldVal: Person) { }
}


上面的使用就相當於:


const s = Symbol('baz')

export const MyComponent = Vue.extend({
  name: 'MyComponent',

  props: {
    checked: Boolean,
    propA: Number,
    propB: {
      type: String,      default: 'default value'
    },
    propC: [String, Boolean],
  },


  methods: {
    addToCount(n){      this.count += n      this.$emit("add-to-count", n)
    },
    resetCount(){      this.count = 0
      this.$emit("reset")
    },
    onChildChanged(val, oldVal) { },
    onPersonChanged(val, oldVal) { }
  },
  watch: {    'child': {
      handler: 'onChildChanged',
      immediate: false,
      deep: false
    },    'person': {
      handler: 'onPersonChanged',
      immediate: true,
      deep: true
    }
  }
})


更加全面的用法參考文件:vue-property-decorator


免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點選


相關文章:
【推薦】 年輕設計師如何做好商業設計