{"_id":"@aimmarc/vue3-class-component","_rev":"3-b431287cd27e043805aae4913e4e0212","name":"@aimmarc/vue3-class-component","dist-tags":{"latest":"1.0.1"},"versions":{"0.0.1":{"name":"@aimmarc/vue3-class-component","version":"0.0.1","author":{"name":"niko.wen","email":"niko.weno@ly.com"},"license":"ISC","_id":"@aimmarc/vue3-class-component@0.0.1","maintainers":[{"name":"tc-fenghuo-im","email":"965964317@qq.com"}],"homepage":"","dist":{"shasum":"2f4755b362e4a6adbce07dcd4ca3d752d7842692","tarball":"https://registry.npmjs.org/@aimmarc/vue3-class-component/-/vue3-class-component-0.0.1.tgz","fileCount":23,"integrity":"sha512-oo8+/FYUW9k5dETZWo7uoZIP4q1WiCMoEY4p1+8SZ4iTwIi8XFN15iT8ZpW5spDsFTfNfr1mERV6nGZw1HWwXg==","signatures":[{"sig":"MEUCIQCsUcqjnXrUwR265BGeF75rbCWktx/DH7Fbn6zqOZ78/gIgLTmH0+QLqPODVtbh8vtzci3DXbhwcexioYcAG0abpM8=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":124330},"main":"lib/index.es.js","types":"lib/src/index.d.ts","gitHead":"5a80b57b722be7e0bd6fca78785448133b03f085","private":false,"scripts":{"test":"echo \"Error: run tests from root\" && exit 1","build":"vite build && tsc"},"_npmUser":{"name":"tc-fenghuo-im","email":"965964317@qq.com"},"repository":{"url":"git@git.17usoft.com:tcyl-web-common/t-web-components.git","type":"git"},"_npmVersion":"8.19.4","description":"> TODO: description","directories":{},"_nodeVersion":"16.20.2","dependencies":{"vue":"^3.2.44","uuid":"^9.0.0","axios":"^0.27.2","dayjs":"^1.11.7","typescript":"^5.5.3","axios-retry":"^4.3.0","class-validator":"~0.14.1","@vue/runtime-dom":"3.2.44","reflect-metadata":"~0.2.2","@vue/compiler-dom":"3.2.44","@vue/runtime-core":"3.2.44"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^4.4.5"},"_npmOperationalInternal":{"tmp":"tmp/vue3-class-component_0.0.1_1727590666276_0.16105595478378643","host":"s3://npm-registry-packages"}},"1.0.0":{"name":"@aimmarc/vue3-class-component","version":"1.0.0","author":{"name":"aimmarc","email":"965964317@qq.com"},"license":"ISC","_id":"@aimmarc/vue3-class-component@1.0.0","maintainers":[{"name":"tc-fenghuo-im","email":"965964317@qq.com"}],"homepage":"","dist":{"shasum":"0477986e65785f64870dd7e407d2fa6d7a501bf0","tarball":"https://registry.npmjs.org/@aimmarc/vue3-class-component/-/vue3-class-component-1.0.0.tgz","fileCount":25,"integrity":"sha512-17ctqIkHuWeIvNbjEpec02RwnnI4MQ1XZZ0ajsPS7O7z3Ap202fiYaLNWd6h7tamWxmnnPmKN19H6wqu6+ZmHw==","signatures":[{"sig":"MEUCIQCTkvl6E98t/wf9mYnpG9M2DRMDnH2GSSbere6ACvQ6MQIga9Fk2/rKy3Xb6C/s11OeFMivB65zDbZhF5dP6n0UsfU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":137909},"main":"lib/index.es.js","types":"lib/src/index.d.ts","gitHead":"be6f6c5c61a3dbf94b207448a1c307fe6a56b8d4","private":false,"scripts":{"test":"echo \"Error: run tests from root\" && exit 1","build":"vite build && tsc"},"_npmUser":{"name":"tc-fenghuo-im","email":"965964317@qq.com"},"repository":{"url":"git@git.17usoft.com:tcyl-web-common/t-web-components.git","type":"git"},"_npmVersion":"10.8.2","description":"> TODO: description","directories":{},"_nodeVersion":"18.20.8","dependencies":{"vue":"^3.2.44","uuid":"^9.0.0","axios":"^0.27.2","dayjs":"^1.11.7","typescript":"^5.5.3","axios-retry":"^4.3.0","class-validator":"~0.14.1","@vue/runtime-dom":"3.2.44","reflect-metadata":"~0.2.2","@vue/compiler-dom":"3.2.44","@vue/runtime-core":"3.2.44"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^4.4.5"},"_npmOperationalInternal":{"tmp":"tmp/vue3-class-component_1.0.0_1754379391622_0.904950713730468","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@aimmarc/vue3-class-component","version":"1.0.1","description":"> TODO: description","author":{"name":"aimmarc","email":"965964317@qq.com"},"homepage":"","license":"ISC","main":"lib/index.es.js","types":"lib/src/index.d.ts","private":false,"repository":{"type":"git","url":"git@git.17usoft.com:tcyl-web-common/t-web-components.git"},"scripts":{"test":"echo \"Error: run tests from root\" && exit 1","build":"vite build && tsc"},"dependencies":{"@vue/compiler-dom":"3.2.44","@vue/runtime-core":"3.2.44","@vue/runtime-dom":"3.2.44","axios":"^0.27.2","axios-retry":"^4.3.0","class-validator":"~0.14.1","dayjs":"^1.11.7","reflect-metadata":"~0.2.2","typescript":"^5.5.3","uuid":"^9.0.0","vue":"^3.2.44"},"devDependencies":{"vite":"^4.4.5"},"_id":"@aimmarc/vue3-class-component@1.0.1","gitHead":"863857424d983ff1a55d28cf5e7c44d595c9c4ae","_nodeVersion":"18.20.8","_npmVersion":"10.8.2","dist":{"integrity":"sha512-O7QLAqyIlr5r5msIDgqBDW/ntdfzOQDwPE8y+DdiNKYuoZyPmsWls1PuCsAoBK9bJ94aJ4IeECQ99WwTm3US/w==","shasum":"bf08bb2cc07fcc2ea0ce6ef02ed37b58494142cc","tarball":"https://registry.npmjs.org/@aimmarc/vue3-class-component/-/vue3-class-component-1.0.1.tgz","fileCount":25,"unpackedSize":149329,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDqq0WgK4QP5feKURZ8Gc7qw5NCu97qapttXNCy7RdmrQIhALbF/w6/23rjw+byRAgN1WWDqB9xhtBBSDM5NWW6PBnv"}]},"_npmUser":{"name":"tc-fenghuo-im","email":"965964317@qq.com"},"directories":{},"maintainers":[{"name":"tc-fenghuo-im","email":"965964317@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vue3-class-component_1.0.1_1754978166070_0.641570868330249"},"_hasShrinkwrap":false}},"time":{"created":"2024-09-29T06:17:46.140Z","modified":"2025-08-12T05:56:06.474Z","0.0.1":"2024-09-29T06:17:46.476Z","1.0.0":"2025-08-05T07:36:31.816Z","1.0.1":"2025-08-12T05:56:06.261Z"},"author":{"name":"aimmarc","email":"965964317@qq.com"},"license":"ISC","repository":{"type":"git","url":"git@git.17usoft.com:tcyl-web-common/t-web-components.git"},"description":"> TODO: description","maintainers":[{"name":"tc-fenghuo-im","email":"965964317@qq.com"}],"readme":"# vue3-class-component\n## 开始\n- vue3-class-component 让你在vue3中使用类的方式来写组件。\n- ​基于TypeScript和装饰器。\n- 通过类的方式来写vue组件。\n- 兼容 stage3 和 stage2 装饰器。\n- 兼容 TypeScript 和 JavaScript 项目。\n- 稳定、安全，根据vue规范将es 类转换成vue composition api。\n- 高性能，项目加载时转换一次，随处可用。\n```Javascript\n<template>\n    <el-button ref=\"buttonRef\" v-click-outside=\"onClickOutside\"> Click me </el-button>\n\n    <el-popover ref=\"popoverRef\" :virtual-ref=\"buttonRef\" trigger=\"click\" title=\"With title\" virtual-triggering>\n        <span> Some content </span>\n    </el-popover>\n</template>\n\n\n<script lang=\"ts\">\nimport { ElPopover, ClickOutside } from 'element-plus';\nimport { Component, toComponent, Vue } from 'vue3-class-component';\n\n@Component({\n    directives: {\n        ClickOutside,\n    },\n})\nclass Hehe extends Vue {\n    popoverRef = this.ref();\n    buttonRef = this.ref();\n\n    onClickOutside() {\n        this.vue.unref(this.popoverRef).popperRef?.delayHide?.();\n    }\n}\n\nexport default toComponent(Hehe);\n</script>\n```\n如果你喜欢已经废弃的 vue-class-component 或 deprecaed vue-property-decorator, 那么请试试这个项目吧！\n欢迎讨论和贡献代码。\n\n## 如何使用？\n### 定义一个类组件\n类组件必须继承这个项目的Vue基类并且应用这个项目的Component​装饰器。\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component';\n\n@Component\nclass MyComponent extends Vue {\n\n}\n\nexport default toComponent(MyComponent);\n```\n\n### 在Vue SFC中使用\n在SFC中(.vue 文件)，使用类组件作为默认导出的内容。\n```Javascript\n<template>\n  <div></div>\n</template>\n<script lang=\"ts\">\n// DO NOT USE <script setup>\n\n//vue options API.\n/*\nimport { defineComponent } from \"vue\";\nexport default defineComponent({});\n*/\n\n//class component\nimport { Component, Vue, toComponent } from 'vue3-class-component';\n\n@Component\nclass MyComponent extends Vue {}\n\nexport default toComponent(MyComponent);\n</script>\n<style>\n</style>\n```\n## 组件\n### 用法\n使用装饰器 Component 来装饰一个类，这个类需要继承Vue基类。\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component';\n\n@Component\nclass MyComponent extends Vue {\n\n}\n\nexport default toComponent(MyComponent);\n```\n### 选项\nComponent传入的对象和defineComponent​传入的一致，除了不能传入setup。\n### 属性\n定义在类组件中的属性均具备响应式，可以当作reactive的属性使用，免去自定义ref和reactive的繁琐。\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component';\n\n@Component\nclass MyComponent extends Vue {\n　　num = 1; // num具备响应式，更改this.num会触发watcher\n}\n\nexport default toComponent(MyComponent);\n```\n### 不需要响应式\n只需要用MarkRaw装饰属性即可让属性失去响应式，原理和reactive的markRaw一致。\n```Javascript\nimport { Component, toComponent, Vue, MarkRaw } from 'vue3-class-component/src/decorators';\n\n@Component({\n    props: {\n        num: {\n            default: 4,\n        },\n    },\n    emits: ['change'],\n})\nclass MyComponent extends Vue {\n    mounted() {\n        this.num3 = 5;\n        console.log(this.num3, '==='); // 此处会打印更改后的值，但不会被track\n    }\n\n    @MarkRaw\n    num3 = 1; // num3不具备响应式\n}\n\nexport default toComponent(MyComponent);\n```\n### name\n​比如name​，就和组件的name​一致：\n```Javascript\n​import { Component, Vue, toComponent } from 'vue3-class-component';  \n@Component({\n　　name: 'my-component',\n}) \nclass MyComponent extends Vue {  }  \nexport default toComponent(MyComponent);\n```\n### emits\n使用emits注入事件，通过this.emit触发。\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component';  \n@Component({\n　　name: 'my-component',\n　　emits: ['my-event']\n}) \nclass MyComponent extends Vue { \n　　mounted() {\n　　　　this.emit('my-event');　\n　　}\n}\nexport default toComponent(MyComponent);\n```\n想要获得emits类型提示，需要将emits作为范型传入：\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component';\n\n@Component({\n    name: 'my-component',\n    emits: ['my-event'],\n})\nclass MyComponent extends Vue<any, ['my-event']> {\n    mounted() {\n        this.emit('my-event');\n    }\n}\nexport default toComponent(MyComponent);\n```\n### components\n和组件的components属性一致，组件必须通过Component注册才能使用，全局组件除外。\n```Javascript\nimport { Component, toComponent, Vue } from 'vue3-class-component';\nimport Test from './Test.vue';\nimport Hehe from './Hehe.vue';\n\n@Component({\n    components: { Test, Hehe },\n})\nclass App extends Vue<any, ['youer-event']> {\n\n}\n\nexport default toComponent(App);\n```\n### derictives\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component/src/decorator';\nimport { ClickOutside } from 'element-plus';\n\n@Component({\n    directives: {\n        ClickOutside,\n    },\n})\nclass MyComponent extends Vue<any, ['my-event']> {\n\n}\n\nexport default toComponent(MyComponent);\n```\n### expose\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component';\n\n@Component({\n    expose: ['hehe'],\n})\nclass MyComponent extends Vue {\n    hehe() {\n        alert(2);\n    }\n}\n\nexport default toComponent(MyComponent);\n```\n### slots、attrs\n通过this调用，\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component/src/decorator';\n\n@Component\nclass MyComponent extends Vue {\n    mounted() {\n        console.log(this.attrs);\n        console.log(this.slots);\n    }\n}\n\nexport default toComponent(MyComponent);\n```\n### props\nprops建议通过element-plus的buildProps进行构建，这样方便将props类型对象转换为ts类型，从而传入范型获得类型提示。\n```Javascript\nimport { Component, Setup, toComponent, Vue, Watch, WatchEffect } from 'vue3-class-component';\nimport { StoreType } from '@/common/interfaces/Store';\nimport { ExtractPropTypes, ref } from 'vue';\nimport { buildProps, definePropType } from 'element-plus';\n\nconst props = buildProps({\n    num: {\n        type: definePropType<number>(Number),\n        default: 0,\n    },\n    num1: {\n        type: definePropType<number>(Number),\n        default: 0,\n    },\n} as const);\n\ntype PropTypes = ExtractPropTypes<typeof props>;\n\n@Component({\n    emits: ['youer-event'],\n    props,\n})\nclass Test extends Vue<PropTypes> {}\n\nexport default toComponent<PropTypes, Test>(Test);\n```\n在类组件中通过this.props（v1.0.0提供支持）使用，在template中直接通过props中的属性使用。\n```Javascript\n...\n@Component({\n    emits: ['youer-event'],\n    props,\n})\nclass Test extends Vue<StoreType, PropTypes> {\n　　...\n　　c = this.computed(() => {\n        return this.props.num + this.props.num1;\n   });\n　　...\n}\n...\n<div>{{ num }}</div>\n```\n### @Prop\n还可以通过Prop装饰器定义props，会被自动补充到Component注入的props中。如果有重名，Prop装饰器优先级更高。\n```Javascript\nimport { Component, Vue, Inject, Prop } from 'vue3-class-component/src/decorators';\n\n@Component\nclass MyComponent extends Vue {\n    @Inject\n    num2!: number;\n\n    @Prop({\n        type: Number,\n        default: 5,\n    })\n    num1!: number;\n\n    mounted() {}\n}\n\nexport default toComponent(MyComponent);\n```\n### setup钩子\n组件提供了setup钩子，方便在setup阶段做一些操作。\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component';\n\n@Component({})\nclass MyComponent extends Vue {\n    setup() {\n        console.log('setup');\n    }\n}\n\nexport default toComponent(MyComponent);\n```\n### @Watch\n提供了一个@Watch方法装饰器，用于简化侦听器逻辑。接收两个参数，key和options，key表示要侦听的当前组件的属性，options和watch的options一致。\n```Javascript\nimport { Component, Vue, toComponent, Watch } from 'vue3-class-component';\n\n@Component\nclass MyComponent extends Vue {\n    num = 1;\n\n    @Watch('num', {\n        immediate: true,\n    })\n    numChange(newVal: number) {\n        console.log(newVal);\n    }\n}\n\nexport default toComponent(MyComponent);\n```\n### @WatchEffect\n用法如下。\n```Javascript\nimport { ComponentOptions, Vue, toComponent, Watch, WatchEffect } from 'vue3-class-component';\n\n@ComponentOptions({})\nclass MyComponent extends Vue {\n    num = 1;\n\n    @WatchEffect({\n　　　　flush: 'post',\n　　 })\n    myWatchEffect() {\n        console.log(this.num);\n    }\n}\n\nexport default toComponent(MyComponent);\n```\n### @Setup\nSetup装饰器可以用于方法和属性，用于方法：方法将在setup阶段被自动执行；用于属性：需要传入回调处理函数，函数需要返回你想赋值给属性的值，允许将Hook作为值供属性使用。\n```Javascript\nimport { Component, toComponent, Setup, Watch, Vue } from 'vue3-class-component';\nimport { ref, type Ref } from 'vue';\nimport { useRouter, useRoute, RouteLocationNormalizedLoaded, Router } from 'vue-router';\n\n@Component\nclass MyComponent extends Vue {\n    // 定义ref\n    @Setup(() => ref(1))\n    num!: Ref<number>;\n    @Watch('num', {\n        immediate: true,\n    })\n    numChange(newVal: number) {\n        console.log(newVal);\n    }\n    // 定义route\n    @Setup(useRoute)\n    route!: RouteLocationNormalizedLoaded;\n    // 定义router\n    @Setup(useRouter)\n    router!: Router;\n    protected mounted(): void {\n        console.log(this.route, this.router, this.num);\n    }\n    // 用于方法\n    @Setup()\n    doSth() {\n        console.log('doSth');\n    }\n}\n\nexport default toComponent(MyComponent);\n```\n### @Inject\n使用@Inject可以很轻松实现属性的注入。\n```Javascript\nimport { Component, toComponent, Vue, Setup, Watch, Inject } from 'vue3-class-component';\nimport { ref, type Ref } from 'vue';\nimport { useRouter, useRoute, RouteLocationNormalizedLoaded, Router } from 'vue-router';\n\n@Component\nclass MyComponent extends Vue {\n    @Inject(0)\n    num!: number;\n}\n\nexport default toComponent(MyComponent);\n```\n### @Provide\n使用如下。\n```Javascript\nimport { Component, toComponent, Vue, Provide } from 'vue3-class-component';\nimport { StoreType } from '@/common/interfaces/Store';\nimport Test from './Test.vue';\nimport Hehe from './Hehe.vue';\n\n@Component({\n    emits: ['youer-event'],\n    components: { Test, Hehe },\n})\nclass App extends Vue<StoreType, any, ['youer-event']> {\n    @Provide\n    num = this.ref(12);\n\n    handleAdd() {\n        this.num.value++;\n    }\n}\n\nexport default toComponent(App);\n```\n使用别名。\n```Javascript\nimport { Component, toComponent, Vue, Provide } from 'vue3-class-component';\nimport { StoreType } from '@/common/interfaces/Store';\nimport Test from './Test.vue';\nimport Hehe from './Hehe.vue';\n\n@Component({\n    emits: ['youer-event'],\n    components: { Test, Hehe },\n})\nclass App extends Vue<StoreType, any, ['youer-event']> {\n    @Provide('num1')\n    num = this.ref(12);\n\n    handleAdd() {\n        this.num.value++;\n    }\n}\n\nexport default toComponent(App);\n```\n## 钩子\nvue3-class-component几乎支持所有Vue原生钩子，用法与同选项式API的钩子相同。\n```Javascript\nimport { Component, Vue, toComponent } from 'vue3-class-component/src/decorators';\n\n@Component\nclass MyComponent extends Vue {\n    num = 1;\n\n    mounted() {\n        console.log(this.num);\n    }\n}\n\nexport default toComponent(MyComponent);\n```\n支持的钩子列表：\n- setup\n- beforeMount\n- ​mounted\n- ​beforeUpdate\n​- updated\n- ​beforeUnmount\n- ​unmounted\n​- activated\n- ​deactivated\n- ​errorCaptured\n## 存取器\n### 用法\n使用get即可实现类似computed的功能。\n```Javascript\nimport { Component, toComponent, Vue, Watch } from 'vue3-class-component';\n\n@Component({\n    emits: ['youer-event'],\n})\nclass App extends Vue<any, ['youer-event']> {\n    handleAdd() {\n        this.num.value++;\n    }\n\n    get propsNum() {\n        return this.props.num;\n    }\n}\n\nexport default toComponent(App);\n```\n### 可写的\n这种写法和computed的set一致。\n```Javascript\nimport { Component, toComponent, Vue, Watch } from 'vue3-class-component';\n\n@Component({\n    emits: ['change'],\n})\nclass App extends Vue<any, ['change']> {\n    handleAdd() {\n        this.num.value++;\n    }\n\n    get propsNum() {\n        return this.props.num;\n    }\n\n    set propsNum(val) {\n        return this.emit('change', val);\n    }\n}\n\nexport default toComponent(App);\n```\n## 试验性API\n### JSX\n支持JSX写法，但目前是实验特性，可能会存在致命bug，请谨慎使用。\n```Javascript\nimport { Component, toComponent, Vue, Setup, Watch, Prop } from 'vue3-class-component';\nimport { Ref, ref } from 'vue';\n\n@Component\nclass TsxTest extends Vue {\n    num = 90;\n    @Setup(() => ref(12))\n    num2!: Ref<number>;\n    @Watch('num')\n    numChange(newVal: number) {\n        console.log(newVal);\n    }\n    num3 = this.ref(99);\n    get nums() {\n        return this.num3;\n    }\n    @Prop({\n        type: Number,\n        default: 10,\n    })\n    prop1!: number;\n\n    handleAdd = () => {\n        this.num++;\n        this.num3.value++;\n        this.num2.value++;\n    };\n\n    render() {\n        return (\n            <>\n                <div>{this.num}</div>\n                <div>{this.num2.value}</div>\n                <div>{this.num3?.value}</div>\n                <div>{this.nums.value}</div>\n                <div>{this.prop1}</div>\n                <button onClick={this.handleAdd}>+</button>\n            </>\n        );\n    }\n}\n\nexport default toComponent(TsxTest);\n```\n## （必看！）注意事项\n### 箭头函数\n框架中不限制箭头函数的使用，但是要注意在装饰器组件中使用箭头函数，更改组件最顶层的属性是不会触发响应式的，这是由于箭头函数的this无法被更改，所以无法被proxy的get set劫持。使用箭头函数会触发警告！\n就算一定要使用箭头函数，也尽量避免直接使用this.xxx = xxx的形式更改属性，这将不会触发响应式。可以使用ref代替普通属性，但还是不建议这样做。","readmeFilename":"README.md"}