最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 使用装饰器和class关键字编写Vue2.x组件

    正文概述 掘金(沧沧凉凉)   2021-03-21   622

    您好,我是沧沧凉凉,是一名前端开发者,目前在掘金、知乎以及个人博客上同步发表一些学习前端时遇到的趣事和知识,欢迎关注。


    相信目前在前端行业中,大部分项目依然是使用的Vue2.x来进行编写,除了很多人不愿意跳出自己的舒适圈去学习新的东西之外,还有一个原因就是Vue2.x比Vue3.x的第三方库丰富太多,因为Vue3 Composition API的关系,导致很多支持Vue2.x的第三方库与Vue3.x都不兼容。

    当然这并不是本篇文章要讲的重点,本篇文章的重点其实是vue-property-decorator这个装饰器库提供的装饰器。

    之所以要使用装饰器的原因是因为装饰器可以极大程度简化Vue组件中各种状态的声明,并且Mixins的引用会变得更加明确(但是通常不推荐在一个项目中大量使用Mixins,因为会大大降低代码的可读性。)


    vue-property-decorator一共有提供以下几种装饰器:

    1. @Prop
    2. @PropSync
    3. @Model
    4. @ModelSync
    5. @Watch
    6. @Provide
    7. @Inject
    8. @ProvideReactive
    9. @InjectReactive
    10. @Emit
    11. @Ref
    12. @VModel
    13. @Component

    **需要值得注意的是,装饰器并不仅仅可以用在ts上面,在js上面一样可以使用!**下面就来看一下这些装饰器的魅力吧:

    1. @Component/Mixins

    使用class关键字来创建组件的基本方法:

    import { Component, Vue, VModel, Mixins } from "vue-property-decorator";
    
    @Component
    export default class C extends Vue {}
    
    // Mixins 括号中引入混入的文件
    @Component
    export default class C extends Mixins() {}
    

    这样就可以创建一个Vue组件,同时如果你使用了TypeScript,那么你会得到更好的类型推断。

    2. @Prop/@Emit

    在本库中最常用的两个装饰器,分别对应Propthis.$emit

    2.1 @Prop

    用法:

    @Component
    export default class YourComponent extends Vue {
      // 下面3个是官方给的用法
      @Prop(Number) readonly propA: number | undefined
      @Prop({ default: 'default value' }) readonly propB!: string
      @Prop([String, Boolean]) readonly propC: string | boolean | undefined
    }
    

    相当于:

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

    我个人比较喜欢使用下面这种写法:

    @Component
    export default class YourComponent extends Vue {
      @Prop({ type: Number, required: true, default: 0 }) readonly propA:
        | number
        | undefined;
    }
    

    @Prop()括号里面接各种参数。

    2.2 @Emit

    一般来讲,要更改Prop的值则需要通过this.$emit("xxx" , value)这种写法,例如:

    change(): void {
      this.$emit("change", 5);
    }
    

    而使用了装饰器后可以这样写:

    @Emit()
    change(): number {
      return 5;
    }
    

    其中return的值就是$emit所要传递的值,如果@Emit()不带参数,则默认将函数名作为$emit所要触发的名称,如果带了参数:例如@Emit("a"),则相当于this.$emit("a", 5);

    同时@Emit装饰器会自动将camelCase(驼峰命名)命名转换为kebab-case。

    3. @PropSync

    因为在Vue2.x里只能拥有一个v-model,所以可以使用.sync关键字来创建多个类似于v-model的双向绑定。

    // 父组件通过:name.sync关键字
    <B :name.sync="name" />
    
    // 子组件
    @PropSync("name") syncName!: string;
    
    @Emit("update:name")
    input(): string {
      return "张三";
    }
    

    对于.sync关键字,可以看官方的文章。

    4. @VModel

    快速创建一个v-model

    import { Component, Vue, VModel } from "vue-property-decorator";
    
    @Component
    export default class C extends Vue {
      @VModel({ type: String }) name!: string;
    }
    

    等同于:

    export default {
      props: {
        value: {
          type: String,
        },
      },
      computed: {
        name: {
          get() {
            return this.value
          },
          set(value) {
            this.$emit('input', value)
          },
        },
      },
    }
    

    就是实现一个v-model双向绑定,只要在子组件中调用this.$emit('input', value)就可以修改传入的值,也可以配合@Emit装饰器使用:

    @Component({
      components: { C },
    })
    export default class B extends Vue {
      @VModel({ type: String }) name!: string;
    
      @Emit()
      input(): string {
        return "张三";
      }
    }
    

    5. @Model/@ModelSync

    5.1 @Model

    从上面的v-model双向绑定我们可以得知,v-model实际上是绑定了一个Prop value属性,和$emit的input属性,如果想将v-model所绑定的这两个属性更改一下名字时,我们就可以使用@Model装饰器:

    import { Vue, Component, Model } from 'vue-property-decorator'
    
    @Component
    export default class YourComponent extends Vue {
      @Model('change', { type: Boolean }) readonly checked!: boolean
    }
    

    上面的代码等同于:

    export default {
      model: {
        prop: 'checked',
        event: 'change',
      },
      props: {
        checked: {
          type: Boolean,
        },
      },
    }
    

    至于model属性,可以参考官方文档。

    5.2 @ModelSync

    该装饰器是结合了@Model@VModel两个装饰器,将两个装饰器的功能合二为一。

    import { Vue, Component, ModelSync } from 'vue-property-decorator'
    
    @Component
    export default class YourComponent extends Vue {
      @ModelSync('checked', 'change', { type: Boolean })
      readonly 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)
          },
        },
      },
    }
    

    6. @Provide/@Inject

    其实我之前是不知道有这两个装饰器的,直到我最近写Vue组件中的值传递比较费劲,突然想起Vue中有Provide/Inject,所以去翻阅了一下Vue中的几种值传递方式,然后又想到我目前正在写的项目虽然是使用js构建,但是使用了vue-property-decorator装饰器库,就去翻阅一下官方文档看是否有这两个装饰器,一翻还真有。

    这里说一下vuex存在的副作用,以及我为啥想要使用Provide/Inject,其实我之前一直是一个vuex党,对于一个复杂状态的传递我搞不清楚就统统使用vuex,但是有时候vuex会将一些简单的逻辑复杂化,因为vuexstate状态的修改理论上都是需要通过Mutation(虽然可以使用对象的方式绕过Mutation直接修改state的值,就跟Prop传递到子组件的对象,子组件也可以修改该对象下的属性一样)。

    同时vuex还带来一个问题,因为vuex中存储的状态是不会因为组件的销毁而自动销毁的,除非刷新浏览器,所以有时候会因为留下了很多状态而引起一些BUG,如果要手动清除这些状态势必又会带来额外的工作量,所以为了解决子孙之间的值传递,我就想到了Provide/Inject

    但是使用Provide/Inject又会引发另一个问题,就是**Provide传递的值因为可以在任何子组件甚至孙组件中通过Inject来进行调用,所以该值的源头找起来就会比较麻烦,所以一定要做好注释!**不然过段时间再看代码你根本找不到该值从哪个父层级中传递而来。

    我曾经接手过一个项目,它是使用$refs的方式进行传值,又没有注释,半找半猜终于找到了该值的源头,过程十分艰辛。

    6.1 @Provide

    该装饰器可以直接声明在类中的变量上,将该属性通过provide传递给子组件。

    例子:

    @Component({
      components: { B },
    })
    export default class A extends Vue {
      @Provide()
      message = {
        content: "",
      };
    }
    

    相当于:

    export default {
      components: { B },
      data() {
        return {
          message: { content: "" },
        };
      },
      provide() {
        return {
          message: this.message,
        };
      },
    };
    

    可以看到简化了非常多的代码,非常方便,同时@Provide()中还可以跟一个String类型的参数,该参数表示了provide()所要传递出去的名字,一旦设置在后面的Inject中就需要使用设置的别名来调用:

    // 父组件
    @Component({
      components: { B },
    })
    export default class A extends Vue {
      // Provide a
      @Provide("a")
      message = {
        content: "",
      };
    }
    
    // 子组件
    export default {
      name: "B",
      components: { C },
      inject: {
        // 这里的值一定要与父组件@Provide()括号中的值对应
        message: "a",
      },
    };
    

    如果不设置别名,则会默认将所装饰的变量名当做Provide的名称。

    6.2 @Inject

    跟上面一样,使用起来非常方便:

    @Component
    export default class C extends Vue {
      @Inject() readonly message!: { content: string };
    }
    

    如果@Inject()括号中不带参数,则默认将后面的变量名作为注入名称。

    也可以像@Inject("a")这样指定对应的名称,同时后面还可以跟一个对象设置其默认值:@Inject({ from: 'optional', default: 默认值 })

    7. @ProvideReactive/@InjectReactive

    可以看到上面演示@Provide/@Inject我是使用了一个message对象,然后修改它的content属性,这是因为Provide/Inject传递的值不是响应式,如果你将message对象换成一个字符串,如下所示:

    export default class A extends Vue {
      @Provide()
      message = "";
    }
    

    则当message被改变时,子组件中调用该数据的界面显示不会有任何变化,换句话说就是子组件其界面不会刷新,那么这个时候@ProvideReactive/@InjectReactive就可以解决这个问题。

    // 父组件
    export default class A extends Vue {
      @ProvideReactive()
      message = "";
    }
    
    // 子组件
    export default class B extends Vue {
      @InjectReactive() message!: string;
    }
    

    你再去页面中尝试,你会发现即使更改了message的值,在子组件调用到的界面也会进行刷新。

    8. @Watch

    这个很简单,就不过多讲解了,一看就会:

    // 需要监听的变量名
    @Watch("data")
    change(): void {
      // 所要执行的语句
    }
    

    同时变量名后还可以接选项:@Watch('person', { immediate: true, deep: true })

    9. @Ref

    跟上面一样,不带参数默认将变量名作为名称,带了参数后则将参数作为名称,变量名作为别名:

    @Ref() aaa!: HTMLElement;
    
    mounted(): void {
      console.log(this.aaa);
    }
    

    10. 最后

    通过对装饰器的学习,对于一些我平时没有了解过或者平时没有注意过的属性有了一定的认知,比如.sync关键字,model属性等。

    使用装饰器会大大降低我们平时写代码时声明Vue组件状态的繁琐性,其实vue-property-decorator装饰器库的使用并不复杂,官方文档也写的比较清楚。但是鉴于很多人不喜欢看官方文档(因为是英文的)而我之前找了几篇好像又没有列出所有的装饰器,所以我才写下了这篇文章,希望大家对于Vue中的装饰器有更进一步的了解。

    参考链接:vue-property-decorator 文档


    起源地下载网 » 使用装饰器和class关键字编写Vue2.x组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元