Vue 裝飾器寫法

依賴庫介紹

vue-class-component(官方文檔)

Vue的官方庫,支持以class的方式來寫vue代碼,它支持下列寫法html

  • methods能夠直接聲明類的成員方法
  • 計算屬性能夠被聲明爲類的屬性訪問器
  • 初始化的data能夠被聲明爲類屬性
  • datarender已經全部的Vue生命週期鉤子能夠做爲類的成員方法
  • 提供的裝飾器Component和處理mixins寫法的方式mixins
  • 全部其餘屬性須要放在裝飾器Component

vue-property-derocator(官方文檔)

Vue社區維護的在vue-class-component的基礎上開發的庫,它是對上一個庫的拓展,添加了一下裝飾器用來處理一些本來須要放在Component中的屬性,下列是它提供的一下裝飾器vue

  • @Prop
  • @PropSync
  • @Provide
  • @Model
  • @Watch
  • @Inject
  • @Emit
  • @Component(來自vue-class-component庫的vue-class-component)
  • Mixins方法(來自vue-class-componentmixins)

vuex-class(官方文檔)

Vue主要貢獻者在vue-class-component基礎上封裝的針對vuex的裝飾器寫法的庫。它提供了4個裝飾器和namespaceios

  • @State
  • @Getter
  • @Mutation
  • @Action
  • namespace

裝飾器使用

Js寫法也可以使用裝飾器寫法,用法相同,只是Ts語法部分會有不一樣;eslint規範也有可能報錯,須要修改部分配置git

安裝

npm i --save vue-class-component
npm i --save vuex-class
npm i --save-dev vue-property-decorator
複製代碼

導入與使用

// 導入部分
import Component, { mixins } from 'vue-class-component'
// 若是用到了vue-property-decorator庫,建議只使用一個庫導入,避免Component在項目中頻繁從兩個不一樣的庫中讀取
import { Component, Prop, Vue } from 'vue-property-decorator'

// 使用部分,@Component必寫
@Component
export default class HelloWorld extends Vue {
}
複製代碼

vue-class-component部分

datamethods計算屬性生命週期鉤子部分
  • 原寫法
export default {
  data () {
    return {
      msg: 'msg'
    }
  },
  methods: {
    add () {}
  },
  mounted() {}, 
  computed: {
    newmsg () {
      return 'new ' + this.msg
    }
  }
}
複製代碼
  • 裝飾器寫法 data與methods直接定義爲類的屬性便可,computed須要定義爲訪問器屬性,mixins方法本來經過mixins屬性導入改成經過繼承
@Component
export default class HelloWorld extends Vue {
  msg!: Number // data
  // 因爲Ts最新版本使用了strictPropertyInitialization,若是變量沒有在構造函數中使用或賦值,都須要添加!,進行顯式賦值斷言
  add () {} // methods
  get newmsg() { // computed
    return this.msg
  }
  mounted() {} // 生命週期鉤子
}
複製代碼
mixins混入方法實現
  • 原寫法
import mix from './mixin'
export default {
  mixins: [mix] // 經過mixins屬性導入
}
複製代碼
  • 裝飾器寫法 經過繼承裝飾器提供的mixins方法處理後的混入方法mix實現
import Component, { mixins } from 'vue-class-component'
import mix from './mixin'

@Component
export class MyComp extends mixins(mix) {
  created () {
    console.log(this.mixinValue) // -> Hello
  }
}
複製代碼
其餘的部分(componentsfliterdirectivesPropWatch等)
  • 原寫法
export default {
  components: {},
  props: {},
  watch: {
    msg (newVal, oldVal) {}
  }
}
複製代碼
  • 裝飾器寫法 其他的部分所有包裹在@Component裝飾器中
@Component({
  components: {},
  props: {},
  watch: {
    new (newVal, oldVal) {}
  }
})
export default class HelloWorld extends Vue {}
複製代碼

vue-property-decorator擴展的裝飾器

@Component(options:ComponentOptions = {})

@Component裝飾器來自vue-class-component,功能沒有變化,在使用了vue-property-derocator後,主要用包括其尚不支持的componentdirectives等屬性github

import {Component,Vue} from 'vue-property-decorator'
import {componentA,componentB} from '@/components'

 @Component({
    components:{
        componentA,
        componentB,
    },
    directives: {
        focus: {
            // 指令的定義
            inserted: function (el) {
                el.focus()
            }
        }
    }
})
export default class YourCompoent extends Vue{}
複製代碼
@Prop(options: (PropOptions | Constructor[] | Constructor) = {})

@Prop裝飾器主要是處理prop屬性接收的父組件傳來的值vuex

  • 接收三個參數
    • Constructor:如StringNumberBoolean等,指定Prop類型
    • Constructor[]:指定Prop的可選類型
    • PropOptionsProptypedefaultrequiredvalidator等配置
  • 原寫法
export default{
    props:{
        propA:String, // propA:Number
        propB:[String,Number],
        propC:{
            type:Array,
            default:()=>{
                return ['a','b']
            },
            required: true,
            validator:(value) => {
                return [
                    'a',
                    'b'
                 ].indexOf(value) !== -1
        }
    }
  }
}

複製代碼
  • @Prop裝飾器寫法
import {Component,Vue,Prop} from vue-property-decorator;

@Component
export default class YourComponent extends Vue {
    @Prop(String)
    propA:string;
    
    @Prop([String,Number])
    propB:string|number;
    
    @Prop({
     type: String, // type: [String , Number]
     default: 'default value', // 通常爲String或Number
      //若是是對象或數組的話。默認值從一個工廠函數中返回
      // defatult: () => {
      // return ['a','b']
      // }
     required: true,
     validator: (value) => {
        return [
          'InProcess',
          'Settled'
        ].indexOf(value) !== -1
     }
    })
    propC:string 
}

複製代碼
@PropSync(propName: string, options: (PropOptions | Constructor[] | Constructor) = {})

@ProSync裝飾器與@Prop功能相似,可是最後@ProSync會生成一個新的計算屬性npm

  • 接收兩個參數
    • propName: string 表示父組件傳遞過來的屬性名;
    • options: Constructor | Constructor[] | PropOptions 與@Prop的第一個參數一致
    • @PropSync須要配合父組件的.sync修飾符使用
  • 原寫法
export default {
  props: {
    name: {
      type: String
    }
  },
  computed: {
    syncedName: {
      get() {
        return this.name
      },
      set(value) {
        this.$emit('update:name', value)
      }
    }
  }
}
複製代碼
  • @ProSync裝飾器寫法
import { Vue, Component, Prop } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  @PropSync('name', { type: String }) syncedName!: string
}

複製代碼
@Model(event?: string, options: (PropOptions | Constructor[] | Constructor) = {})

@Model裝飾器是處理vuemodel屬性,該屬性能夠再一個組件上自定義v-model,實現自定義組件prop屬性的雙向綁定element-ui

  • 接收兩個參數
    • event: string 事件名。
    • options: Constructor | Constructor[] | PropOptions 與@Prop的第一個參數一致。

父級組件部分(template部分)數組

<my-input :value="value" @change="val=>value=val"></my-input>
複製代碼

myInput組件部分promise

  • 原寫法(2.2.0+新增)
<template>
  <div class="hello"> // 直接傳值給父級方法後再父級中修改prop,避免了直接在組件中修改prop <input type="text" :value="value" @input="$emit('change', $event.target.value)" /> </div> </template>
export default {
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    value: {
      type: String,
      default: ''
    }
  }
}
複製代碼
  • @Model裝飾器寫法
<template>
  <div class="hello"> <input type="text" :value="value" @input="$emit('change', $event.target.value)" /> </div> </template>

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

@Component
export default class HelloWorld extends Vue {
  @Model('change', {
    type: String
  })
  value!: String
}
複製代碼
@Watch(path: string, options: WatchOptions = {})

@Watch裝飾器是處理vue的監聽屬性,監聽發生在beforeCreate以後created以前

  • 接收兩個參數
    • path: 監聽的屬性
    • options?: WatchOptions={},監聽的配置信息
      • immediate?: boolean 偵聽開始以後是否當即調用該回調函數;
      • deep?: boolean 被偵聽的對象的屬性被改變時,是否調用該回調函數;
  • 原寫法
export default {
  watch: {
    child: [
      {
        handler: 'onChildChanged',
        immediate: false,
        deep: false
      }
    ],
    person: [
      {
        handler: 'onPersonChanged1',
        immediate: true,
        deep: true
      },
      {
        handler: 'onPersonChanged2',
        immediate: false,
        deep: false
      }
    ]
  },
  methods: {
    onChildChanged(val, oldVal) {},
    onPersonChanged1(val, oldVal) {},
    onPersonChanged2(val, oldVal) {}
  }
}
複製代碼
  • @Watch裝飾器寫法
import { Vue, Component, Watch } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  @Watch('child')
  onChildChanged(val: string, oldVal: string) {}

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

  @Watch('person')
  onPersonChanged2(val: Person, oldVal: Person) {}
}
複製代碼
@Emit(event?: string)

@Emit裝飾器專門處理$emit方法($emit方法並無規定必定要在這裏使用)

  • 接收的參數
    • 接受一個可選參數,該參數是$Emit的第一個參數,充當事件名。若是沒有提供這個參數,$Emit會將回調函數名的camelCase轉爲kebab-case,並將其做爲事件名
    • @Emit會將回調函數的返回值做爲第二個參數,若是返回值是一個Promise對象,$emit會在Promise對象被標記爲resolved以後觸發
    • @Emit的回調函數的參數,會放在其返回值以後,一塊兒被$emit當作參數使用。
  • 原寫法
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    addToCount(n) {
      this.count += n
      this.$emit('add-to-count', n)
    },
    resetCount() {
      this.count = 0
      this.$emit('reset')
    },
    returnValue() {
      this.$emit('return-value', 10)
    },
    onInputChange(e) {
      this.$emit('on-input-change', e.target.value, e)
    },
    promise() {
      const promise = new Promise(resolve => {
        setTimeout(() => {
          resolve(20)
        }, 0)
      })
      promise.then(value => {
        this.$emit('promise', value)
      })
    }
  }
}
複製代碼
  • 裝飾器寫法
import { Vue, Component, Emit } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  count = 0
  @Emit()
  addToCount(n: number) {
    this.count += n
  }
  @Emit('reset')
  resetCount() {
    this.count = 0
  }
  @Emit()
  returnValue() {
    return 10
  }
  @Emit()
  onInputChange(e) {
    return e.target.value
  }
  @Emit()
  promise() {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(20)
      }, 0)
    })
  }
}

複製代碼
@Ref(refKey?: string)

@Refthis.$ref寫法的封裝,接收一個可選參數,用來指向元素或子組件的引用信息。若是沒有提供這個參數,會使用裝飾器後面的屬性名充當參數

  • 原寫法
export default {
  computed: {
    loginForm: {
      cache: false,
      get() {
        return this.$refs.loginForm
      }
    },
    passwordForm: {
      cache: false,
      get() {
        return this.$refs.changePasswordForm
      }
    }
  }
}
複製代碼
  • 裝飾器寫法
import { Vue, Component, Ref } from 'vue-property-decorator'
import { Form } from 'element-ui'

@Componentexport default class MyComponent extends Vue {
  @Ref() readonly loginForm!: Form
  @Ref('changePasswordForm') readonly passwordForm!: Form

  public handleLogin() {
    this.loginForm.validate(valide => {
      if (valide) {
        // login...
      } else {
        // error tips
      }
    })
  }
}

複製代碼
@Provide/ @Inject

這兩個裝飾器是對ProvideInject寫法的封裝

  • Provide與Inject

    • 當父組件不便於向子組件傳遞數據,就把數據經過Provide傳遞下去,而後子組件經過Inject來獲取
    • 以容許一個祖先組件向其全部子孫後代注入一個依賴,不論組件層次有多深,並在起上下游關係成立的時間裏始終生效
    • 一旦父級注入的屬性修改,子組件沒法獲取變化
  • 原寫法

const symbol = Symbol('baz')

export const MyComponent = Vue.extend({
  inject: {
    foo: 'foo',
    bar: 'bar',
    optional: { from: 'optional', default: 'default' },
    [symbol]: symbol
  },
  data() {
    return {
      foo: 'foo',
      baz: 'bar'
    }
  },
  provide() {
    return {
      foo: this.foo,
      bar: this.baz
    }
  }
})
複製代碼
  • 裝飾器寫法
import { Component, Inject, Provide, Vue } from 'vue-property-decorator'
const symbol = Symbol('baz')

@Component
export class MyComponent extends Vue {
  @Inject() readonly foo!: string
  @Inject('bar') readonly bar!: string
  @Inject({ from: 'optional', default: 'default' }) readonly optional!: string
  @Inject(symbol) readonly baz!: string

  @Provide() foo = 'foo'
  @Provide('bar') baz = 'bar'
}
複製代碼
@ProvideReactive/ @InjectReactive

這兩個裝飾器的功能與@Provide/ @Inject相似,可是它注入的屬性是響應式的,父級注入的屬性一旦修改,子組件能夠獲取到變化

  • 裝飾器寫法
const key = Symbol()
@Component
class ParentComponent extends Vue {
  @ProvideReactive() one = 'value'
  @ProvideReactive(key) two = 'value'
}

@Component
class ChildComponent extends Vue {
  @InjectReactive() one!: string
  @InjectReactive(key) two!: string
}
複製代碼

vuex-class的使用

@namespace命名空間
  • 直接使用裝飾器
@Component
class MyComponent extends Vue{
    @namespace('test').State foo!: string;
}
複製代碼
  • 引用裝飾器賦值
const TestModule = namespace("test")

@Component
class MyComponent extends Vue{
    @TestModule.State foo!: string;
}
複製代碼
@State狀態映射(數據)
  • 直接映射 @State foo 和 @State("foo") foo 相同
@Component
class MyComponent extends Vue{
    @namespace('test').State foo!: string;
}
複製代碼
  • 獲取計算後的state,state映射到組件時,是放在computed下的,所以自己爲計算屬性
@Component
class MyComponent extends Vue{
    /* 只從 state 獲取*/
    @namespace('test').State(state=>state.foo) foo!: string;
    /*從state和getters上獲取*/
    @namespace('test').State((state,getters)=>state.foo+'getter:'+getters.getFoo) svsgfoo!: string;
}
複製代碼
  • 內部使用namespace 若是不想在外部使用namespace,可使用參數傳遞namespace
@Component
class MyComponent extends Vue{
    @State("foo",{namespace:"test"}) foo!: string;
}
複製代碼
@Getter計算屬性

和State相似,但不支持再次計算,只能獲取值

@Component
class MyComponent extends Vue{
    @State("foo",{namespace:"test"}) foo!: string;
}
複製代碼
@Action異步計算,觸發Mutation
@Component
class MyComponent extends Vue{
   @Action("test/setFoo") setFoo!: Function;
}
複製代碼

action能夠配合promise + async/await 一同使用

// 原寫法
actions:{
    async queryState({commit},token){
        let result = await Axios.get("url",{data:{ token }})
        commit('state',result.data)
        return result.data
    }
}

// 裝飾器寫法
@Component
class MyComponent extends Vue{
   private token:string="best";
   @Action queryState!: Function;
   
   async onQueryStateClick(){
       let data = await this.queryState(this.token)
       // todo ...
   }
}
複製代碼
@Mutation直接計算state數據
@Component
class MyComponent extends Vue{
   @Action("test/setFoo") setFoo!: Function;
}
複製代碼
原代碼
export default new Vuex.Store({
  state: { /* 狀態庫 */ },
  mutations: { /* 同步計算庫 使用commit觸發 */ },
  actions: { /* 異步計算庫 使用dispatch觸發 */ },
  getters: { /* 計算屬性 */ },
  modules: { /* 分模塊 */
    test: {
      namespaced: true, /* 開啓module模式 */
      state: {
        foo: "this is foo"
      },
      getters: {
        getFoo(state) { 
          return state.foo;
        }
      },
      mutations: {
        setFoo(state, foo) {
          state.foo = foo;
        }
      },
      actions: {
        setFoo({ commit }, foo) {
          setTimeout(() => {
            commit('setFoo',foo)
          }, 1e3);
        }
      }
    }
  }
})

複製代碼
相關文章
相關標籤/搜索