关于vue-class-component和vue-property-decorator以及vuex-class的使用

关于vue-class-component和vue-property-decorator的使用

tip: vue-property-decorator 依赖于 vue-class-component(vue-property-decorator 是在 vue-class-component的基础上进行扩展,所以vue-class-component有的vue-property-decorator也有,使用时可以只导入vue-property-decorator,但安装包时两个包都需要下载)

  • 基础写法

    import {
          
           Vue, Component, Prop, PropSync } from 'vue-property-decorator'
    @Component
    export default class HelloWorld extends Vue{
          
          
      // a. 声明变量
      // 注意:当变量的初始值为undefined时,它不是响应式的
      // 解决办法:
      // 1. 赋值为null
      // 2. 使用data的钩子函数   data(){ return { hello: undefined } }
      message = 'hello world'
    
      // b. 声明函数
      hello() {
          
          
        console.log('Hello World!')
      }
    
      // c. 计算属性
      // 注意:computed对象中有 cache属性,设置为false表示不缓存(即每次取数据都重新执行一遍函数),默认为true(即只有当相关数据改变,才会重新执行函数)
      get name(){
          
          
        return this.firstName + ' ' + this.lastName
      }
      set name(){
          
          
        const splitted = value.split(' ')
        this.firstName = splitted[0]
        this.lastName = splitted[1] || ''
      }
    
      // d. Hooks
      mounted(){
          
          }
    
      // c. 
    
      // 其他选项写在 @Component({})中
    }
    
  • 注册除了生命周期以外的钩子函数

    // 注册钩子函数(推荐新建register.ts文件,在main.js中引入 --> 组件定义前引入)
    import Component from 'vue-class-component'
    Component.registerHooks([
      'beforeRouteEnter',
      'beforeRouteLeave',
      'beforeRouteUpdate'
    ])
    
    // 注册完之后就可以在组件类中直接使用(与生命周期钩子函数相同)
    
  • 自定义装饰器

    import {
          
           createDecorator } from "vue-class-component";
    
    export const Log = createDecorator((options, key) => {
          
          
      const originalMethod = (options.methods as any)[key]; // 得到装饰器装饰的方法
    
      // 包装原方法
      (options.methods as any)[key] = function wrapperMethod(...args: any) {
          
          
        console.log(`Invoked: ${
            
            key}(`, ...args, ")");
        // Invoke the original method.
        originalMethod.apply(this, args);
      };
    });
    
    
    // 自定义组件中使用
    // 绑定事件:<button @click="hello($event,2,3,4)">hello</button>
    // 调用hello方法
    @Log
    hello(e: any, value: string) {
          
          
      console.log('hello');
    }
    
  • extend
    组件的扩展 --> 继承父组件(extends) --> 可以使用父组件的变量

    // super.js   父组件
    import Vue from 'vue'
    import Component from 'vue-class-component'
    
    @Component
    export default class Super extends Vue {
          
          
      superValue = 'Hello'
    }
    
    
    // 子组件
    import Super from './super'
    import Component from 'vue-class-component'
    
    @Component
    export default class HelloWorld extends Super {
          
          
      created() {
          
          
        console.log(this.superValue) // -> Hello   可以直接通过this访问父组件的变量(相当于直接继承了父组件的所有script内容)
      }
    }
    
  • mixins

    // 新建mixins文件存放
    // mixins.ts    
    import Vue from 'vue'
    import {
          
           Component } from 'vue-property-decorator'
    
    @Component
    export class Hello extends Vue {
          
          
      hello = 'Hello'
    }
    
    @Component
    export class World extends Vue {
          
          
      world = 'World'
    }
    
    
    // 在组件中使用
    import {
          
           Component, mixins } from 'vue-property-decorator'
    import {
          
           Hello, World } from './mixins'
    
    // `mixins` can receive any number of arguments.
    @Component
    export class HelloWorld extends mixins(Hello, World) {
          
          
      created () {
          
          
        console.log(this.hello + ' ' + this.world + '!') // -> Hello World!
      }
    }
    
  • 注意事项:
    a. 在类形式的组件中,this只是Vue实例的代理对象(this指向Vue实例)
    b. 不能使用箭头函数去定义方法(this在箭头函数中指向undefined)

  • 显示赋值断言 “!”
    前提情节: ts2.7引入了一个新的flag:–strictPropertyInitialization。这个flag检查并确保一个类在初始化时,每一个属性都必须在构造器内初始化,或者在属性定义时赋予初始值

    class C {
          
          
        foo: number;
        bar = "hello";
        baz: boolean;
    //  ~~~
    //  Error! Property 'baz' has no initializer and is not definitely assigned in the
    //         constructor.
    
        constructor() {
          
          
            this.foo = 42;
        }
    }
    
    // foo 通过构造器初始化了,bar 有初始值,baz 都没有,因此提示错误。
    

    解决办法:
    a. 一种解决办法是把 foo 的类型改成 boolean | undefined
    b. foo!: number; (由用户自己保证,这个属性在使用前会初始化,类型检查不管这个属性) —> 官方把这个属性称为 Definite Assignment Assertions,中文文档翻译为显式赋值断言。

  • vue-property-class中的装饰器

    1. @Prop
      @Prop({
          
          
        default: '没有参数传过来',
      })
      readonly name!: string | number;
    
      @Prop()
      name: string | undefined
    
    1. @PropSync
    // 父组件
    <About :ppp.sync="ppp"></About>  // 注意要加上.sync
    
    // 子组件
    @PropSync('ppp')  // 括号里传的是父组件传过来的参数名称
    paa!: string   // paa是重新定义的名称,值与ppp相同
    
    handleClick(){
          
          
      this.paa = 'sssss'  // 此时修改paa,即修改ppp,父组件的ppp也会修改
    }
    
    
    
    // @PropSync('ppp')
    // paa!: string    相当于以下写法
    props:{
          
          
      ppp:{
          
          
        type: String
      }
    }
    computed:{
          
          
      paa:{
          
          
        get(){
          
          return this.ppp},
        set(value){
          
          
          this.$emit('update:ppp',value)
        }
      }
    }
    
    1. @Model
      补充:Vue中的model属性
      通常在自定义组件上使用v-model = “值”,相当于在自定义组件中默认绑定了 :value=‘值’ 和 @input=“val => foo = val”,在自定义组件中使用model属性,可以修改默认绑定的事件和value参数( model:{ prop: ‘自己定义参数(默认是value)’,event: ‘自己定义触发事件(默认是input)’} )

    @Model的使用

    // 自定义组件中使用
    @Model('change',{
          
           type:Boolean }) checked!: boolean
    //相当于
    export default {
          
          
      model: {
          
          
        prop: 'checked',
        event: 'change'
      },
      props: {
          
          
        checked: {
          
          
          type: Boolean
        }
      }
    }
    
    1. @ModelSync
      @ModelSync是在@Model的基础上添加了触发事件,使得子组件可以修改父组件传过来的值(同时修改父组件的该值)
    @ModelSync('checked', 'change', {
          
          type: Boolean} ) checkedValue!:boolean
    // 相当于
    export default {
          
          
      model: {
          
          
        prop: 'checked',
        event: 'change'
      },
      props: {
          
          
        checked:{
          
          
          type: Boolean
        }
      },
      computed: {
          
          
        checkedValue: {
          
          
          get(){
          
          
            return this.checked
          },
          set(value) {
          
          
            this.$emit('change', value)
          }
        }
      }
    }
    
    1. @VModel
      补充:与@ModelSync相比,@VModel不修改默认的value和input,而可以修改父组件传来的值(同时修改父组件的该值)
    @VModel({
          
           type: String }) name!: String
    // 相当于
    export default {
          
          
      props: {
          
          
        value: {
          
          
          type: String
        }
      },
      computed: {
          
          
        name: {
          
          
          get() {
          
          
            return this.value
          },
          set(value){
          
          
            this.$emit('input', value)
          }
        }
      }
    }
    
    1. @Watch
    @Watch('name')
    onNameChange(newVal:string, oldVal:string){
          
          }
    
    @Watch('person', {
          
           immediate:true, deep:true })
    onPersonChange(newVal:string, oldVal:string){
          
          }
    
    // 相当于
    watch:{
          
          
      name(newVal, oldVal){
          
          },
      person:{
          
          
        immediate:true,
        deep:true,
        handler(newVal,oldVal){
          
          }
      }
    }
    
    1. @Provide
    @Provide() foo = 'foo' 
    @Provide('baz') bar = 'bar' // 传入的'baz'相当于重新命名,不传以'bar'为名
    //相当于
    export default {
          
          
      data(){
          
          
        return {
          
          
          foo: 'foo',
          bar: 'bar'
        }
      },
      provide(){
          
          
        return {
          
          
          foo: this.foo,
          baz: this.bar
        }
      }
    }
    
    1. @Inject
    @Inject() readonly foo!: string
    @Inject('bar') readonly bar!: string  // 参数表示Provide的属性名,第二个bar可以理解为重命名
    // 相当于
    export default {
          
          
      inject: {
          
          
        foo: 'foo',
        bar: 'bar'
      }
    }
    
    1. @ProvideReactive
      同@Provide,只是变成响应式的
    2. @InjectReactive
      同@Inject,只是变成响应式的
    3. @Emit
      • description:The functions decorated by @Emit e m i t t h e i r r e t u r n v a l u e f o l l o w e d b y t h e i r o r i g i n a l a r g u m e n t s . I f t h e r e t u r n v a l u e i s a p r o m i s e , i t i s r e s o l v e d b e f o r e b e i n g e m i t t e d . I f t h e n a m e o f t h e e v e n t i s n o t s u p p l i e d v i a t h e e v e n t a r g u m e n t , t h e f u n c t i o n n a m e i s u s e d i n s t e a d . I n t h a t c a s e , t h e c a m e l C a s e n a m e w i l l b e c o n v e r t e d t o k e b a b − c a s e . ( 被 @ E m i t 装 饰 的 函 数 , 会 触 发 emit their return value followed by their original arguments. If the return value is a promise, it is resolved before being emitted. If the name of the event is not supplied via the event argument, the function name is used instead. In that case, the camelCase name will be converted to kebab-case. (被@Emit装饰的函数,会触发 emittheirreturnvaluefollowedbytheiroriginalarguments.Ifthereturnvalueisapromise,itisresolvedbeforebeingemitted.Ifthenameoftheeventisnotsuppliedviatheeventargument,thefunctionnameisusedinstead.Inthatcase,thecamelCasenamewillbeconvertedtokebabcase.(@Emit,emit方法并且将该函数return的值以及该函数所带的参数作为参数依次传入,如果函数返回的是promise对象, e m i t 会 在 r e s o l v e d 之 后 触 发 ) ( 如 果 @ E m i t ( ) 中 没 有 事 件 名 称 , 那 么 它 装 饰 的 函 数 名 称 会 作 为 emit会在resolved之后触发) (如果@Emit()中没有事件名称,那么它装饰的函数名称会作为 emitresolved)(@Emit(),emit触发的函数名称)
      @Emit()
      addToCount(n:number){
              
              
        this.count + = n
      }
      // 相当于
      methods:{
              
              
        addToCount(n){
              
              
          this.count + = n;
          this.$emit('add-to-count',n)
        }
      }
      
      @Emit('reset')
      resetCount(){
              
              
        this.count = 0
      }
      // 相当于
      methods: {
              
              
        this.count = 0;
        this.$emit('reset')
      }
      
      @Emit()
      returnValue(){
              
              
        return 10
      }
      // 相当于
      methods: {
              
              
        this.$emit('return-value', 10)
      }
      
      @Emit()
      onInputChange(e){
              
              
        return e.target.value
      }
      // 相当于
      methods: {
              
              
        this.$emit('on-input-change', e.target.value, e)
      }
      
      @Emit()
      fetchData() {
              
              
        return new Promise((resolve) => {
              
              
          setTimeout(() => {
              
              
            resolve(20)
          },0)
        })
      }
      // 相当于
      methods: {
              
              
        fechData(){
              
              
          const promise = new Promise((resolve) => {
              
              
            setTimeout(() => {
              
              
              resolve(20)
            },0)
          })
      
          promise.then((value) => {
              
              
            this.$emit('fetchData',value)
          })
        }
      }
      
    4. @Ref
      @Ref() 
      readonly home!: Home   // Home组件
      // 相当于
      computed(){
              
              
        home: {
              
              
          cache:false,
          get(){
              
              
            return this.$refs.home as Home
          }
        }
      }
      
      @Ref('aButton')   // 如果没有传入aButton,则在this.$refs上找button,传入则找aButton
      readonly button!: HTMLButton  // HTMLButton为组件名
      // 相当于
      computed(){
              
              
        button: {
              
              
          cache:false,
          get(){
              
              
            return this.$refs.aButton as HTMLButton
          }
        }
      }
      
    5. @Component (provided by vue-class-component)
    6. Mixins (provided by vue-class-component)

vuex-class

import {
    
     namespace } from 'vuex-class'
const {
    
     State, Action, Mutation } = namespace('path/to/module')
@State('foo')
foo!: string  //store.state.path.to.module.foo

import {
    
     State, Action, Mutation } from 'vuex-class'
@State('bar') stateBar
this.stateBar   // store.state.bar

@Action('foo') actionFoo
this.actionFoo({
    
    value:true})  // store.dispatch('foo',{value:true})

@Mutation('foo') mutationFoo
this.mutationFoo({
    
    value:true}) // store.commit('foo',{value:true})

猜你喜欢

转载自blog.csdn.net/qq_36303110/article/details/111618487