组件 = 模板 + 业务逻辑

各位网友大家好,今天给大家推荐我刚发布的 Vue 3 开发库 ------ vue-clazz-decorator

名字里的 "clazz" 是 class 的谐音梗,为什么不直接叫 vue-class-decorator 呢?很简单,因为这个已经有人叫了。

这个库就干一件事:让你用 类 + 装饰器 写 Vue 3 组件,同时把模板和逻辑彻底解开

这个库的主要特色

  • 通过类和装饰器来写 Vue 组件
  • 方便地复用模板和逻辑
  • 保证自定义类型的纯净
  • 支持多种装饰器编译方式

创建组件

这个库的核心思想就是 【组件 = 模板 + 业务逻辑 】。createComponent 这个函数就是围绕这个思想设计的,在避开 Vue 3 不能用 class 组件这个限制的同时,顺便把模板和逻辑的复用也安排得明明白白。

先看看 Vue 3 里同类库的写法,往往长这样

tsx 复制代码
class HelloPage {
  render() {
    return xxxxxx;
  }
}

export default toVue(HelloPage);

能用,但总觉得拧巴:

  • render 方法硬生生塞进业务类里,UI 和逻辑搅在一起,类本身就不纯粹了;
  • 外面还得套一层转换函数,多余,但又不能没有。

我换了个更干脆的思路:模板归模板,逻辑归逻辑,两者彻底分开,最后拼一下就是组件。

tsx 复制代码
@ViewModel
export class CounterViewModel {
    @State
    public count = 0;

    public increment() {
        this.count++;
    }
}
export function CounterView(props: CounterViewModel) {
    return <div>
        <p>Count: {props.count}</p>
        <button onClick={props.increment}>+1</button>
    </div>;
}

export const Counter = createComponent(CounterView, CounterViewModel);

业务逻辑全在 ViewModel 里,就是个干干净净的普通类;模板就是个纯渲染函数,不碰任何业务实现。最后 createComponent 轻轻一粘,就是个标准 Vue 组件。

这么拆开之后,很多事情就顺了:

  • 业务类想单测直接实例化测,不用挂载组件、不用模拟 DOM;
  • 同一份逻辑可以配多套模板,同一份模板也能套不同逻辑;

优雅的逻辑复用

复用逻辑,直接 use() 把另一个 ViewModel 组合进来,天然带命名空间,互不干扰。

ts 复制代码
@ViewModel
export class FooViewModel { ... }

@ViewModel
export class BarViewModel {
    public foo1 = use(FooViewModel);
    public foo2 = use(FooViewModel);
}

分页、筛选、弹窗这类通用逻辑,抽一次就能到处用,依赖关系明明白白。

组件生命周期:灵活且不设限

使用注解声明生命周期方法,不强制固定方法名。更爽的是,一个生命周期钩子可以绑定多个方法,进一步保证了类的纯净度和内聚性

ts 复制代码
@ViewModel
export class FooViewModel {
    @OnDidCreate
    protected init() {
        console.log("ViewModel created");
    }

    @OnDidMount
    protected fetchData() {
        console.log("拉数据");
    }
    @OnDidMount
    protected initDict() {
        console.log("初始化字典");
    }
}

数据模型 + 元数据能力

前面说的 ViewModel 是管组件逻辑的,那纯数据呢?前端处理后端 JSON 时往往需要许多转化规则。于是我干脆把模型层也一并做进库里了,从接口数据到业务实例,一条龙用 Class + 装饰器搞定。

模型层声明

@Model 标记一个数据模型类,字段的映射规则、格式化、类型转换全用装饰器写在类上。后端返回的原始数据,丢进 reactive() 直接就是带类型、带业务方法的响应式实例,再也不用自己手写转换函数了。

ts 复制代码
@Model
export class UserBo {
    @JsonProperty("user_id")  // 下划线字段自动映射
    public id: string;

    @JsonFormat("yyyy-MM-dd") // 日期自动格式化解析
    public birthday: Date;

    @Type(Dept)               // 嵌套对象自动实例化
    public dept: Dept;
}

// 后端原始数据直接转成响应式模型实例
const user = reactive(response.data, UserBo);

自定义注解,扩展性拉满

库内置了完整的元数据系统,你可以像 Java 那样自己写注解,给字段打标签,运行时直接读取配置。

做动态表单、配置化表格、低代码页面的时候特别好用 ------ 模型类写完,表单和表格直接照着元数据自动渲染,不用重复写配置。

ts 复制代码
// 一行代码定义一个自定义注解
const Label = (text: string) => metadata('label', text);
const Required = metadata('required', true);

@Model
export class UserBo {
    @Label("用户ID")
    @Required
    public id: string;

    @Label("用户名")
    public name: string;
}

// 运行时直接读取所有字段元数据
getMetadataValues(UserBo);
// { id: { label: "用户ID", required: true }, name: { label: "用户名" } }

编译方式随便选,我都兜底了

简单说,不管你项目是 TS 还是 Babel,是老版装饰器还是新提案,直接装上就能跑,不用为编译环境折腾配置。内部 6 套 vitest 配置覆盖了这些组合,不是写着玩的。

编译方式 proposal experimental experimental + emitMetadata
typescript ✅ 支持 ✅ 支持 ✅ 支持
babel ✅ 支持 ✅ 支持 ✅ 支持

常见问题

Q:内部用的是 setup 还是 Options API?

纯 setup 实现,所有测试都是在 __VUE_OPTIONS_API__: false 环境下跑的。ViewModel 不是 Vue 的子类,就是个被 setup () 消费的普通 class 实例。

Q:有 React 版吗?

有,但 React 版是我公司内部用的。这次开源的 Vue3 版本,是我吸取了之前 React 版的经验教训,完全重构的,设计上更成熟。

Q:你这刚发布的东西,能直接上生产吗?

不建议。

如果你认同我的设计思路,我的建议是:可以先点个关注、收个藏。等我把上层管理端和移动端的框架都完整重构到 Vue3 上的时候,才是我真正推荐大家上手用的时候。

这只是整个方案的底层基石,后面还有更上层的东西要放出来。


最后

项目刚发,还热乎着。

欢迎去仓库逛逛,有想法、有 bug 都可以提。更欢迎点个 Star 蹲后续,等上层框架出来了,直接上手成品体验会好得多。

相关推荐
二月龙2 小时前
移动端 H5 页面开发:响应式适配 + 低版本兼容实战指南
前端
小强19882 小时前
HTML5 新表单全解:日期、手机号、颜色选择器
前端
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(二):目录结构、初始化 GIT、设计并开发配置系统
前端·后端·go
鱼人2 小时前
HTML5 本地存储终极指南
前端
超绝大帅哥2 小时前
React的Fiber是什么? Vue为什么不需要Fiber ?
前端
星沉远浦2 小时前
用Gemini高效解决Java代码报错难以定位的问题
java
yingyima2 小时前
正则表达式分组与捕获:凌晨3点服务器报警的解决方案
前端
swipe3 小时前
从 0 到 1 理解 React 虚拟列表:定高、不定高与 Canvas 版本完整拆解
前端·javascript·面试
铁皮饭盒4 小时前
Bun执行python代码
前端·javascript·后端