> ## Content Index
> Fetch the complete content index at: https://wayne-wu.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Power Coding：从装饰器实验到业务 Model 层
- URL: https://wayne-wu.com/power-coding/
- Published: 2023-06-02T09:46:59.000Z
- Updated: 2026-08-24T05:40:32.000Z
- Description: Power Coding 从 TypeScript 装饰器实验，演化为公司内部几乎所有前端项目稳定使用的共享 Model 层，让数据转换、缓存和业务行为拥有统一的落点。
- Author: Wayne Wu
- Tags: #project

[Power Coding](https://github.com/WayneWu98/power-coding?ref=wayne-wu.com) 是我在 2023 年开始的一个 TypeScript 实验，也是后来公司共享 Model 层的起点。

当时做后台页面，经常会遇到一种情况：同一份用户数据，在 interface 里定义一次，在 API 文件里转换一次，到了表单和表格又要重新描述。字段改名或结构发生变化后，这些地方都需要同步修改，很容易遗漏。

我想试试把这些原本就属于业务数据的逻辑放回 Model。它不只负责类型，还知道如何从接口数据创建实例、如何转换回请求参数，以及自己具有什么行为。Power Coding 就是围绕这个想法写出来的。

🧩

公开仓库记录的是这套方案的起点。它后来被整理为公司内部共享 Model 模块，目前已在几乎所有前端项目中稳定运行。

## 让 Model 成为数据边界

项目使用 `@Model` 标记模型，`@Field` 描述字段类型和转换规则。`BaseModel.from()` 把接口返回的普通对象转换为 Model，`toPlain()` 再把实例转换成可以提交给接口的对象。

```ts
@Model()
class UserProfile extends BaseModel {
  nickname: string
}

@Model()
@Derive(CRUDDeriver('/users'))
class User extends BaseModel {
  id: number

  @Field({ type: UserProfile })
  profile: UserProfile
}

interface User extends CRUD<User> {}
```

Model、字段转换与 CRUD Deriver 的基础组合

经过 `User.from(raw)` 以后，`profile` 不再是一个普通对象，而是 `UserProfile` 实例。页面不需要知道接口原始数据是怎样嵌套的，也不用在每个请求回调里重新构造对象。

`@Field` 还可以处理字段重命名、自定义转换、单向忽略、默认值以及对象的展开和嵌套。日期、可选字段或前后端命名不一致时，规则写在对应字段上。对页面来说，这些差异已经在数据进入系统时被处理完成。

`clone()`、`merge()` 和 `mix()` 则用于编辑状态。例如修改用户资料时，可以克隆一份 Model 交给表单，保存成功后再合并回原实例。这样既保留 Model 的类型和行为，也不会让输入过程直接污染当前状态。

## 用 Deriver 组合通用行为

仅仅解决数据转换还不够。很多 Model 会重复实现增删改查、分页、滚动加载或缓存，所以项目又加入了 `@Derive`。

`CRUDDeriver` 根据接口地址为 Model 提供 `get`、`create`、`update` 和 `delete`。分页与滚动列表也可以通过 Deriver 获得刷新、加载更多和结束状态。业务代码仍然可以在 Model 中补充自己的方法，不需要为了复用通用行为继承一组层级很深的基类。

装饰器在运行时可以为 Class 增加方法，但 TypeScript 不会自动识别这些新成员，因此还需要用同名 interface 补充 `CRUD<User>` 等类型。这不算特别优雅，却清楚划分了运行时行为和静态类型之间的边界。

后来进入实际业务后，缓存也采用了同样的方式。一个 Model 可以同时派生请求和缓存能力，从本地恢复的数据仍然会被还原成原来的 Model，重新请求后也能直接合并到当前实例。页面只决定什么时候读取缓存或刷新数据，不需要自己维护两套结构。

## 装饰器背后的构建插件

Power Coding 中比较特殊的一部分，是自定义的 Vite 插件 `emit-reflect-metadata`。

项目需要在运行时知道字段类型、字段列表和初始化器，但 Vite 默认使用的 esbuild 不会生成完整的装饰器元数据。只写 `@Field()`，并不足以让系统知道一个字段对应的是字符串、嵌套 Model 还是 Model 数组。

这个插件使用 TypeScript Compiler API 分析带 `@Model` 的 Class，在 Vite 继续编译前补入 `design:type`、`design:fields` 和 `design:initializer`。因此，大部分字段不需要重复手写 `type`，`BaseModel.default()` 也能根据初始化器创建具有正确默认值的实例。

后续几次改动主要都在完善这部分：只处理明确标记的 Model、识别嵌套字段、复用 TypeScript Program，并通过 MagicString 保留 Source Map。装饰器让业务代码看起来更简单，但真正支撑这种声明方式的是编译阶段的静态分析。

## 在实际业务中的使用

GitHub 仓库记录的是早期原型。这套方案后来被整理为公司内部的共享 Model 模块，目前几乎所有前端项目都在使用，并且已经在实际业务中稳定运行。

不同项目面对的数据各不相同，但需要处理的问题很接近：接口字段与前端命名不一致、响应中包含嵌套对象、日期和文件需要转换、部分字段只能单向传递，以及缓存中的普通数据需要恢复为可以继续使用的实例。共享 Model 层为这些问题提供了统一做法，项目不必重新实现一套转换逻辑。

它也让接口、状态管理和缓存使用同一种数据结构。数据从接口进入后先恢复成 Model，业务代码围绕 Model 工作，需要提交或保存时再统一序列化。字段或规则发生变化时，修改集中在模型定义中，不需要逐个检查页面、Store 和请求回调。

[TopWidgets⁺ 创作平台](https://x.xiaozujian.com/?ref=wayne-wu.com) 是其中一个较复杂的例子。平台中的大部分数据定义都使用 Model，作品会经过编辑、IndexedDB、自动备份、项目归档、真机调试和发布等流程。Model 层让这些环节共享同一套字段与转换规则，避免每条链路维护自己的数据格式。更完整的产品架构可以参考[《TopWidgets⁺ 创作平台：技术架构与实现》](https://wayne-wu.com/topwidgets-visual-editor/)。

进入业务后，这套方案也逐渐收紧了边界。早期原型曾把 Ant Design Table 配置和校验反馈放进 `Field`，最终版本移除了这些 View 层依赖，只保留数据结构、转换、缓存和与接口直接相关的行为。布局、交互与组件状态继续由 View 负责，Model 因此可以脱离具体组件库，在不同项目中复用。

> Power Coding 的公开仓库不是最终版本，但它记录了这套方案的起点。后续在公司内部几乎所有前端项目中的长期使用，证明这种 Model 组织方式不仅可以稳定工作，也确实让数据规则更集中，让不同项目之间形成一致的开发方式。

## 项目链接

[GitHub - WayneWu98/power-codingPower Coding 的公开原型与源码。![](https://github.githubassets.com/favicons/favicon.png)GitHubWayneWu98![](https://opengraph.githubassets.com/fd196919c5c9896f7aedc85cdb73de12908f7b8d642c9720255c4c4e5da1a8dc/WayneWu98/power-coding)](https://github.com/WayneWu98/power-coding?ref=wayne-wu.com)

GitHub 仓库

[TypeScript 装饰器实践：用 Class Model 组织 OOP 开发以 TypeScript 装饰器为核心，介绍 Class Model、声明式序列化、字段映射和接口调用。![](https://wayne-wu.com/favicon.ico)Wayne WuWayne Wu![](https://wayne.cellnet.cloud/content/images/size/w1200/2026/08/oop-development-with-decorators-1.webp)](https://wayne-wu.com/oop-development-with-decorators/)

设计思考

[TopWidgets⁺ 创作平台：可视化编辑器的技术架构与实现从编辑器内核、本地项目归档到移动端交付，拆解 TopWidgets⁺ 创作平台的完整技术架构。![](https://wayne-wu.com/favicon.ico)Wayne WuWayne Wu![](https://wayne.cellnet.cloud/content/images/size/w1200/2026/08/topwidgets-visual-editor-feature-borderless.png)](https://wayne-wu.com/topwidgets-visual-editor/)

实际业务案例