npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

vue3-decorator

v1.0.0

Published

- vue3-class-component 让你在vue3中使用类的方式来写组件。 - ​基于TypeScript和装饰器。 - 通过类的方式来写vue组件。 - 兼容 stage3 和 stage2 装饰器。 - 兼容 TypeScript 和 JavaScript 项目。 - 稳定、安全,根据vue规范将es 类转换成vue composition api。 - 高性能,项目加载时转换一次,随处可用。 ```Javascript <template> <el-button ref="butt

Downloads

5

Readme

vue3-class-component

开始

  • vue3-class-component 让你在vue3中使用类的方式来写组件。
  • ​基于TypeScript和装饰器。
  • 通过类的方式来写vue组件。
  • 兼容 stage3 和 stage2 装饰器。
  • 兼容 TypeScript 和 JavaScript 项目。
  • 稳定、安全,根据vue规范将es 类转换成vue composition api。
  • 高性能,项目加载时转换一次,随处可用。
<template>
    <el-button ref="buttonRef" v-click-outside="onClickOutside"> Click me </el-button>

    <el-popover ref="popoverRef" :virtual-ref="buttonRef" trigger="click" title="With title" virtual-triggering>
        <span> Some content </span>
    </el-popover>
</template>


<script lang="ts">
import { ElPopover, ClickOutside } from 'element-plus';
import { Component, toComponent, Vue } from 'vue3-class-component';

@Component({
    directives: {
        ClickOutside,
    },
})
class Hehe extends Vue {
    popoverRef = this.ref();
    buttonRef = this.ref();

    onClickOutside() {
        this.vue.unref(this.popoverRef).popperRef?.delayHide?.();
    }
}

export default toComponent(Hehe);
</script>

如果你喜欢已经废弃的 vue-class-component 或 deprecaed vue-property-decorator, 那么请试试这个项目吧! 欢迎讨论和贡献代码。

如何使用?

定义一个类组件

类组件必须继承这个项目的Vue基类并且应用这个项目的Component​装饰器。

import { Component, Vue, toComponent } from 'vue3-class-component';

@Component
class MyComponent extends Vue {

}

export default toComponent(MyComponent);

在Vue SFC中使用

在SFC中(.vue 文件),使用类组件作为默认导出的内容。

<template>
  <div></div>
</template>
<script lang="ts">
// DO NOT USE <script setup>

//vue options API.
/*
import { defineComponent } from "vue";
export default defineComponent({});
*/

//class component
import { Component, Vue, toComponent } from 'vue3-class-component';

@Component
class MyComponent extends Vue {}

export default toComponent(MyComponent);
</script>
<style>
</style>

组件

用法

使用装饰器 Component 来装饰一个类,这个类需要继承Vue基类。

import { Component, Vue, toComponent } from 'vue3-class-component';

@Component
class MyComponent extends Vue {

}

export default toComponent(MyComponent);

选项

Component传入的对象和defineComponent​传入的一致,除了不能传入setup。

属性

定义在类组件中的属性均具备响应式,可以当作reactive的属性使用,免去自定义ref和reactive的繁琐。

import { Component, Vue, toComponent } from 'vue3-class-component';

@Component
class MyComponent extends Vue {
  num = 1; // num具备响应式,更改this.num会触发watcher
}

export default toComponent(MyComponent);

不需要响应式

只需要用MarkRaw装饰属性即可让属性失去响应式,原理和reactive的markRaw一致。

import { Component, toComponent, Vue, MarkRaw } from 'vue3-class-component/src/decorators';

@Component({
    props: {
        num: {
            default: 4,
        },
    },
    emits: ['change'],
})
class MyComponent extends Vue {
    mounted() {
        this.num3 = 5;
        console.log(this.num3, '==='); // 此处会打印更改后的值,但不会被track
    }

    @MarkRaw
    num3 = 1; // num3不具备响应式
}

export default toComponent(MyComponent);

name

​比如name​,就和组件的name​一致:

​import { Component, Vue, toComponent } from 'vue3-class-component';  
@Component({
  name: 'my-component',
}) 
class MyComponent extends Vue {  }  
export default toComponent(MyComponent);

emits

使用emits注入事件,通过this.emit触发。

import { Component, Vue, toComponent } from 'vue3-class-component';  
@Component({
  name: 'my-component',
  emits: ['my-event']
}) 
class MyComponent extends Vue { 
  mounted() {
    this.emit('my-event'); 
  }
}
export default toComponent(MyComponent);

想要获得emits类型提示,需要将emits作为范型传入:

import { Component, Vue, toComponent } from 'vue3-class-component';

@Component({
    name: 'my-component',
    emits: ['my-event'],
})
class MyComponent extends Vue<any, ['my-event']> {
    mounted() {
        this.emit('my-event');
    }
}
export default toComponent(MyComponent);

components

和组件的components属性一致,组件必须通过Component注册才能使用,全局组件除外。

import { Component, toComponent, Vue } from 'vue3-class-component';
import Test from './Test.vue';
import Hehe from './Hehe.vue';

@Component({
    components: { Test, Hehe },
})
class App extends Vue<any, ['youer-event']> {

}

export default toComponent(App);

derictives

import { Component, Vue, toComponent } from 'vue3-class-component/src/decorator';
import { ClickOutside } from 'element-plus';

@Component({
    directives: {
        ClickOutside,
    },
})
class MyComponent extends Vue<any, ['my-event']> {

}

export default toComponent(MyComponent);

expose

import { Component, Vue, toComponent } from 'vue3-class-component';

@Component({
    expose: ['hehe'],
})
class MyComponent extends Vue {
    hehe() {
        alert(2);
    }
}

export default toComponent(MyComponent);

slots、attrs

通过this调用,

import { Component, Vue, toComponent } from 'vue3-class-component/src/decorator';

@Component
class MyComponent extends Vue {
    mounted() {
        console.log(this.attrs);
        console.log(this.slots);
    }
}

export default toComponent(MyComponent);

props

props建议通过element-plus的buildProps进行构建,这样方便将props类型对象转换为ts类型,从而传入范型获得类型提示。

import { Component, Setup, toComponent, Vue, Watch, WatchEffect } from 'vue3-class-component';
import { StoreType } from '@/common/interfaces/Store';
import { ExtractPropTypes, ref } from 'vue';
import { buildProps, definePropType } from 'element-plus';

const props = buildProps({
    num: {
        type: definePropType<number>(Number),
        default: 0,
    },
    num1: {
        type: definePropType<number>(Number),
        default: 0,
    },
} as const);

type PropTypes = ExtractPropTypes<typeof props>;

@Component({
    emits: ['youer-event'],
    props,
})
class Test extends Vue<PropTypes> {}

export default toComponent<PropTypes, Test>(Test);

在类组件中通过this.props(v1.0.0提供支持)使用,在template中直接通过props中的属性使用。

...
@Component({
    emits: ['youer-event'],
    props,
})
class Test extends Vue<StoreType, PropTypes> {
  ...
  c = this.computed(() => {
        return this.props.num + this.props.num1;
   });
  ...
}
...
<div>{{ num }}</div>

@Prop

还可以通过Prop装饰器定义props,会被自动补充到Component注入的props中。如果有重名,Prop装饰器优先级更高。

import { Component, Vue, Inject, Prop } from 'vue3-class-component/src/decorators';

@Component
class MyComponent extends Vue {
    @Inject
    num2!: number;

    @Prop({
        type: Number,
        default: 5,
    })
    num1!: number;

    mounted() {}
}

export default toComponent(MyComponent);

setup钩子

组件提供了setup钩子,方便在setup阶段做一些操作。

import { Component, Vue, toComponent } from 'vue3-class-component';

@Component({})
class MyComponent extends Vue {
    setup() {
        console.log('setup');
    }
}

export default toComponent(MyComponent);

@Watch

提供了一个@Watch方法装饰器,用于简化侦听器逻辑。接收两个参数,key和options,key表示要侦听的当前组件的属性,options和watch的options一致。

import { Component, Vue, toComponent, Watch } from 'vue3-class-component';

@Component
class MyComponent extends Vue {
    num = 1;

    @Watch('num', {
        immediate: true,
    })
    numChange(newVal: number) {
        console.log(newVal);
    }
}

export default toComponent(MyComponent);

@WatchEffect

用法如下。

import { ComponentOptions, Vue, toComponent, Watch, WatchEffect } from 'vue3-class-component';

@ComponentOptions({})
class MyComponent extends Vue {
    num = 1;

    @WatchEffect({
    flush: 'post',
   })
    myWatchEffect() {
        console.log(this.num);
    }
}

export default toComponent(MyComponent);

@Setup

Setup装饰器可以用于方法和属性,用于方法:方法将在setup阶段被自动执行;用于属性:需要传入回调处理函数,函数需要返回你想赋值给属性的值,允许将Hook作为值供属性使用。

import { Component, toComponent, Setup, Watch, Vue } from 'vue3-class-component';
import { ref, type Ref } from 'vue';
import { useRouter, useRoute, RouteLocationNormalizedLoaded, Router } from 'vue-router';

@Component
class MyComponent extends Vue {
    // 定义ref
    @Setup(() => ref(1))
    num!: Ref<number>;
    @Watch('num', {
        immediate: true,
    })
    numChange(newVal: number) {
        console.log(newVal);
    }
    // 定义route
    @Setup(useRoute)
    route!: RouteLocationNormalizedLoaded;
    // 定义router
    @Setup(useRouter)
    router!: Router;
    protected mounted(): void {
        console.log(this.route, this.router, this.num);
    }
    // 用于方法
    @Setup()
    doSth() {
        console.log('doSth');
    }
}

export default toComponent(MyComponent);

@Inject

使用@Inject可以很轻松实现属性的注入。

import { Component, toComponent, Vue, Setup, Watch, Inject } from 'vue3-class-component';
import { ref, type Ref } from 'vue';
import { useRouter, useRoute, RouteLocationNormalizedLoaded, Router } from 'vue-router';

@Component
class MyComponent extends Vue {
    @Inject(0)
    num!: number;
}

export default toComponent(MyComponent);

@Provide

使用如下。

import { Component, toComponent, Vue, Provide } from 'vue3-class-component';
import { StoreType } from '@/common/interfaces/Store';
import Test from './Test.vue';
import Hehe from './Hehe.vue';

@Component({
    emits: ['youer-event'],
    components: { Test, Hehe },
})
class App extends Vue<StoreType, any, ['youer-event']> {
    @Provide
    num = this.ref(12);

    handleAdd() {
        this.num.value++;
    }
}

export default toComponent(App);

使用别名。

import { Component, toComponent, Vue, Provide } from 'vue3-class-component';
import { StoreType } from '@/common/interfaces/Store';
import Test from './Test.vue';
import Hehe from './Hehe.vue';

@Component({
    emits: ['youer-event'],
    components: { Test, Hehe },
})
class App extends Vue<StoreType, any, ['youer-event']> {
    @Provide('num1')
    num = this.ref(12);

    handleAdd() {
        this.num.value++;
    }
}

export default toComponent(App);

钩子

vue3-class-component几乎支持所有Vue原生钩子,用法与同选项式API的钩子相同。

import { Component, Vue, toComponent } from 'vue3-class-component/src/decorators';

@Component
class MyComponent extends Vue {
    num = 1;

    mounted() {
        console.log(this.num);
    }
}

export default toComponent(MyComponent);

支持的钩子列表:

  • setup
  • beforeMount
  • ​mounted
  • ​beforeUpdate ​- updated
  • ​beforeUnmount
  • ​unmounted ​- activated
  • ​deactivated
  • ​errorCaptured

存取器

用法

使用get即可实现类似computed的功能。

import { Component, toComponent, Vue, Watch } from 'vue3-class-component';

@Component({
    emits: ['youer-event'],
})
class App extends Vue<any, ['youer-event']> {
    handleAdd() {
        this.num.value++;
    }

    get propsNum() {
        return this.props.num;
    }
}

export default toComponent(App);

可写的

这种写法和computed的set一致。

import { Component, toComponent, Vue, Watch } from 'vue3-class-component';

@Component({
    emits: ['change'],
})
class App extends Vue<any, ['change']> {
    handleAdd() {
        this.num.value++;
    }

    get propsNum() {
        return this.props.num;
    }

    set propsNum(val) {
        return this.emit('change', val);
    }
}

export default toComponent(App);

试验性API

JSX

支持JSX写法,但目前是实验特性,可能会存在致命bug,请谨慎使用。

import { Component, toComponent, Vue, Setup, Watch, Prop } from 'vue3-class-component';
import { Ref, ref } from 'vue';

@Component
class TsxTest extends Vue {
    num = 90;
    @Setup(() => ref(12))
    num2!: Ref<number>;
    @Watch('num')
    numChange(newVal: number) {
        console.log(newVal);
    }
    num3 = this.ref(99);
    get nums() {
        return this.num3;
    }
    @Prop({
        type: Number,
        default: 10,
    })
    prop1!: number;

    handleAdd = () => {
        this.num++;
        this.num3.value++;
        this.num2.value++;
    };

    render() {
        return (
            <>
                <div>{this.num}</div>
                <div>{this.num2.value}</div>
                <div>{this.num3?.value}</div>
                <div>{this.nums.value}</div>
                <div>{this.prop1}</div>
                <button onClick={this.handleAdd}>+</button>
            </>
        );
    }
}

export default toComponent(TsxTest);

(必看!)注意事项

箭头函数

框架中不限制箭头函数的使用,但是要注意在装饰器组件中使用箭头函数,更改组件最顶层的属性是不会触发响应式的,这是由于箭头函数的this无法被更改,所以无法被proxy的get set劫持。使用箭头函数会触发警告! 就算一定要使用箭头函数,也尽量避免直接使用this.xxx = xxx的形式更改属性,这将不会触发响应式。可以使用ref代替普通属性,但还是不建议这样做。