惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
P
Palo Alto Networks Blog
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
NISL@THU
NISL@THU
L
Lohrmann on Cybersecurity
有赞技术团队
有赞技术团队
The GitHub Blog
The GitHub Blog
C
Cisco Blogs
B
Blog
Microsoft Azure Blog
Microsoft Azure Blog
Recent Announcements
Recent Announcements
Simon Willison's Weblog
Simon Willison's Weblog
T
Tenable Blog
Know Your Adversary
Know Your Adversary
Spread Privacy
Spread Privacy
WordPress大学
WordPress大学
月光博客
月光博客
Latest news
Latest news
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threat Research - Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
I
InfoQ
D
Darknet – Hacking Tools, Hacker News & Cyber Security
W
WeLiveSecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
酷 壳 – CoolShell
酷 壳 – CoolShell
U
Unit 42
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
Google DeepMind News
Google DeepMind News
Apple Machine Learning Research
Apple Machine Learning Research
Attack and Defense Labs
Attack and Defense Labs
罗磊的独立博客
T
The Exploit Database - CXSecurity.com
I
Intezer
GbyAI
GbyAI
Jina AI
Jina AI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
Google Online Security Blog
Google Online Security Blog
Engineering at Meta
Engineering at Meta
D
Docker
Recent Commits to openclaw:main
Recent Commits to openclaw:main
小众软件
小众软件
云风的 BLOG
云风的 BLOG
爱范儿
爱范儿
Project Zero
Project Zero

V2EX

我用 AI 写代码,但终端管理反而成了累赘——于是我做了 codux - V2EX [调研] 各位在公司都用什么 ide 和 agent 写代码? 老运维 share 一个运维平台 新电脑 brew install node 之后,一个小设置可以提升对供应链投毒的防御 - V2EX GLM-Coding 调用持续报错: z.ai 的 Lite 套餐几乎无法使用,官方 Pro/Max 是否稳定? - V2EX 上海漕河泾内推,本组有 2 个 hc,一个后端,一个前端,预算都是 20k 左右,不打卡,氛围好 如果 V2EX 上有一组不永久保存聊天记录(比如只保存 7 天或者 24 小时)的聊天室,那么会开启哪些有用或者有趣的可能? - V2EX gemini cli 貌似挂了,一直返回 403 - V2EX 第一次在自媒体上赚到钱 收集了最近在使用的低价 GPT, Gemini,邮箱等 AI 会员的小店合集 讨论个大实话:现在企业还在说 AI 编程提效 20%, 30%的,真的太落后,没用懂 AI。因为包括很多前沿公司,已经狂奔到提效 200%-500%的情况 [招聘][远程][币安] 前端/后端/QA/iOS/Android 至少 3 年以上经验 目前有大量 HC 欢迎投递 Chatgpt Pro 用量用不完的可以开这些设置 面试的时候好像遇到钓鱼了,给各位避个坑 cursor 年续费 22 号到期, 自动续费是否还是老的计次套餐呢 - V2EX 被两件破事毁掉的一下午,琐碎的内耗消磨人的精力 使用 Planet 存储 Codex 的会话或者重要信息 - V2EX 如果业务部门领导不要你开发功能,而是要求你教会它用 claude code 开发功能,你会怎么做? 分享一个 MacOS 接绿联 CM818 USB 转 DP 转接器使用感受 - V2EX 我的 HR 朋友 10 年老 Java ,非全大专,大家帮忙看看简历 开源了一个 AI 口语练习工具,音素级发音评分,完全免费可自部署 V2EX 上有哪些你觉得很有趣、印象深刻的妹纸? 字节为啥不出个国内版 Vercel? 有在大马的朋友吗? 问个运营商问题 你们在有领导的公司大群发过的最大胆的消息是什么 公司裁员,目前没有工作。想试试摆摊,做一个移动鲜啤打酒车 我的硬盘 Memblaze Pblaze 5 Linux 下不识别,给 Linux 内核提交了补丁, AI 说有望被合并 - V2EX 只有我一个人觉得 codex 不好用? 做了个 AI + 真人专家监督的广告投放平台 Auxora, 7 个品牌跑出 6x ROAS 如何走出至亲的离世 Claude Web 端貌似 claude-opus-4-7 偷偷上了? 现在 Apple 开发者帳號應該是用哪个地区会更好? - V2EX 用回测筛选因子的一点经验分享 给女儿 vibe 了一个故事类的 app,做完发现,这类应用似乎上线难度极大? - V2EX 手机格式化 bitget 钱包没了,里面开通的银行卡还有机会拿到吗 - V2EX [送码] TransVoice - 我的第一款 App 上架啦!实时转写+翻译+字幕,会议听课好助手! PictureHub 高清摄影作品的画廊 Planet 的第一个使用 macOS 26 SDK 构建的 Insider 版本 20260416-1 - V2EX 成都二手房是不是在涨价,有点坐不住了 - V2EX claude 生态(skill mcp plugin)等 Studio Display XDR VESA 适配器脱落 有在用印度区 applestore 的大哥嘛,请教一下礼品卡去哪里买呢 - V2EX 我好像知道京东家政爆火的原因了 - V2EX 薅了公司的 a 家 api key,用机场 ip 做代理容易被封吗 如何在初期就识别 HR 在刷 KPI,没打算招你? [分享]精心打造一个 AI 编程知识库(算法/设计模式/提示词/Skills),助力程序员转型 港版 iPhone 在国内支持联通 5GA 吗?在广东用 想办港卡 AI 对 it 行业影响太大了 我做了个把照片变成 iOS 小组件贴纸的 App ChatGPT Pro 5x 套餐 量真的很足! I have found a method to directly generate advertising video materials using scripts 在小城市开个店,给人写软件,有前途吗 chrome 最新的 147 版直接卡爆炸了 - V2EX 为什么厂家不在 skill/mcp 这类的工具中塞广告呢?这样不是可以大赚嘛? minimax 真是脸都不要了,工作日下午 14:00 定时开启 529,脸都不要了。训练模型居然占用用户使用时间 外资非核心部门 vs 另一家外资的核心部门,该跳吗? iTad 标签 扩展 加小动作 ? - V2EX 去年 H200 能买,不让买是代替快出来了? - V2EX AI 赛事通 - 2026 年 4 月中国区新增 AI 竞赛和黑客松汇总 - V2EX V2EX › 登录 现在安卓开发都在做啥 - V2EX 浏览器插件 沉浸式翻译 是不支持自定义模型了吗? - V2EX Codex 里的 GPT5.4 也能降智?上午让它改两个问题,改了一个小时了, plus 额度用了一半了还是没改好,和前几天用的体感完全不一样。要它改的问题也不复杂。服了。 目前有使用 claude code 的收到人脸认证的吗 - V2EX 分享一个自己做的 Nginx 管理工具,实时请求动态预览!(无奈市面上实在找不到好用的,自己撸了个) - V2EX claude code 崩了么? 今天在反重力上用 claude 一点都不丝滑,有同样的感受吗? opencode 消息周知插件 今天 claude opus 和前两天比,质的飞跃 - V2EX 999 包月价? - V2EX 一个版本, 50 项更新:我们几乎重做了整个播放页 本地大模型多大显存够用? GOGDNS 一款简易的私人 DNS 服务器 - V2EX API key (GLM) 怎么使用 claude code desktop ? Claude 这样订阅有问题吗 - V2EX 帮我爸找回了一篇赛博兰亭集序 求推荐稳定、高性价比使用 Claude Opus 4.6 的渠道/平台 搞个云端 claude code 防止 封号 - V2EX 用 Claude 要实名了,内地用户怎么办? OpenAI Plus 和 Team 都缩水了吗 海外 Android 手机有什么好用的国内第三方应用市场推荐吗 - V2EX 把电脑伪装成电视,用 DLNA 投屏拿到视频号直播流地址 - V2EX claude 认证莫慌 北京互联网法院有什么攻略么?起诉北京智谱华章科技股份有限公司退款可行么? - V2EX Claude 开始引入身份验证 求 vscode 做笔记软件的插件推荐 - V2EX 讯飞星辰的 Coding Plan 如何? Anthropic 宣布在 Claude 平台推行身份验证机制 科普一下低价 gpt 是怎么来的 有没有长期关注 Claude 的朋友,我建了一个 Channel 自动抓取 Claude Team 的推文 啃了那篇 54 页的 Agent Harness 综述, 给大伙讲个省流版 现在那家的 coding plan 还能买到 是不是最近会有什么更聪明的大模型要发布了呀? 用多了 AI 后,有没有觉得 AI 生成的文章有很强的既视感? 如何 实践 Harness 工程? 今日份 GPT 5.4 笑话 如何建一个自己的号池,让 cursor 真正实现 token 自由 写了三个月 Agent Harness,我终于敢让 Claude Code 全自动写代码了
告别 Django Admin!这个 NodeJS 全栈框架让你在 DTO 中直接配置 Table/Form 渲染
zhennann · 2026-05-21 · via V2EX

一、CRUD 页面的重复劳动,该结束了

做中后台的同学一定不陌生这个流程:建一张表,然后写列表页、搜索栏、新建表单、编辑表单、详情页……几乎每个字段都要在前端和后端各写一遍——后端定义字段类型和校验规则,前端再对应写一遍表格列、表单控件、搜索条件。

Django Admin 的出现缓解了这个问题,它能根据 Model 自动生成增删改查页面。但代价也不小:服务端模板渲染、前端技术栈老旧、界面定制困难、不支持 SSR 。

如果能在定义数据结构的同时,直接声明这个字段在表格里怎么显示、在表单里用什么控件,岂不是一步到位?

VonaJS 做的就是这件事。

二、VonaJS 是什么

VonaJS 是一款全栈框架,支持在同一个代码库中构建 SSR/SPA/Web 网站/Admin 中后台。它的核心能力有两点:

  • DTO 动态推断与生成:基于 Zod4 的统一 Schema ,一份定义同时用于参数校验、OpenAPI 文档生成、Table/Form 渲染、数据序列化与脱敏
  • CRUD 动态渲染:根据 DTO 中声明的渲染元数据,自动生成列表页、条目页、搜索表单,底层基于 Tanstack Table / Tanstack Form / Tanstack Query 的最佳实践

技术栈方面,后端是 Koa + Knex + Zod4 + Redis ,前端是 Vue3 + Vite8 + Tanstack 全家桶,UI 层可搭配 Daisyui/Tailwindcss/Quasar/Vuetify 。

一句话:用 DTO 驱动 CRUD 渲染,让中后台开发从"写页面"变成"配字段"。

三、Entity:字段渲染的起点

VonaJS 的渲染配置从 Entity 开始。每个字段的渲染元数据直接写在字段定义旁边,一目了然:

@Entity<IEntityOptionsStudent>('demoStudent', {
  openapi: { title: $locale('Student') },
  fields: {
    id: $makeMetadata(ZovaRender.order(1, 'core')),
    iid: $makeMetadata(ZovaRender.visible(false)),
    deleted: $makeMetadata(ZovaRender.visible(false)),
    createdAt: $makeMetadata(
      ZovaRender.order(-2, 'max'),
      ZovaRender.field('basic-date:formFieldDate'),
      ZovaRender.cell('basic-date:date'),
    ),
    updatedAt: $makeMetadata(
      ZovaRender.order(-1, 'max'),
      ZovaRender.field('basic-date:formFieldDate'),
      ZovaRender.cell('basic-date:date'),
    ),
  },
})
export class EntityStudent extends EntityBase {
  @Api.field(
    v.title($locale('Name')),
    v.required(),
    v.min(2),
    ZovaRender.order(1),
    ZovaRender.cell('basic-table:actionView'),
  )
  name: string;

  @Api.field(
    v.title($locale('Description')),
    v.optional(),
    ZovaRender.order(2),
    ZovaRender.field('basic-select:formFieldSelect', {
      placeholder: 'Please Select',
      items: [
        { title: 'Male', value: 1 },
        { title: 'Female', value: 2 },
      ],
    }),
    ZovaRender.cell('basic-select:select', {
      items: [
        { title: 'Male', value: 1 },
        { title: 'Female', value: 2 },
      ],
    }),
  )
  description?: string;
}

逐行看关键的渲染配置:

配置 含义
ZovaRender.order(1) 字段排在第 1 位
ZovaRender.visible(false) 隐藏该字段(不渲染)
ZovaRender.cell('basic-table:actionView') 表格中渲染为可点击查看的链接
ZovaRender.cell('basic-date:date') 表格中渲染为日期格式
ZovaRender.field('basic-select:formFieldSelect', {...}) 表单中渲染为下拉选择框,并传入选项数据
ZovaRender.field('basic-date:formFieldDate') 表单中渲染为日期选择器

核心思路:渲染配置紧跟字段定义,改一个字段时校验规则和渲染行为一起调整,不用再去前端组件里翻找对应位置。

ZovaRender.cell() 控制表格列怎么显示,ZovaRender.field() 控制表单用什么控件。配置格式统一为 模块名:组件名,并可通过第二个参数传入组件 props ,比如下拉框的选项列表、class 、style 等。

Entity 定义了字段级的渲染元数据,DTO 则负责把这些字段组装成完整的页面。一个 DTO 就是一个页面,页面结构通过 blocks 声明式定义。

1. 列表页

@Dto<IDtoOptionsStudentSelectResItem>({
  blocks: [
    ZovaRender.block('basic-page:blockPage', {
      blocks: [
        ZovaRender.block('basic-page:blockFilter'),
        ZovaRender.block('basic-page:blockToolbarBulk', {
          actions: [ZovaRender.tableActionBulk('basic-table:actionCreate')],
        }),
        ZovaRender.block('basic-page:blockTable'),
        ZovaRender.block('basic-page:blockPager'),
      ],
    }),
  ],
})
export class DtoStudentSelectResItem extends $Dto.get(() => ModelStudent) {
  @Api.field(
    v.title($locale('Operations')),
    ZovaRender.order(1, 'max'),
    ZovaRender.cell('basic-table:actionOperationsRow', {
      actions: [
        ZovaRender.tableActionRow('basic-table:actionUpdate'),
        ZovaRender.tableActionRow('basic-table:actionDelete'),
      ],
    }),
  )
  _operationsRow?: unknown;
}

这个列表页由四个 block 组成:搜索区 → 批量操作栏 → 数据表格 → 分页器,从上到下依次排列。操作栏里放了一个"新建"按钮,表格行末尾自动追加"编辑"和"删除"操作列。

DTO 继承自 $Dto.get(() => ModelStudent),这意味着列表的字段直接从 Model (进而从 Entity )继承,不需要重复定义。

2. 搜索条件

@Dto<IDtoOptionsStudentSelectReq>({
  openapi: { filter: { table: 'demoStudent' } },
  fields: {
    name: $makeSchema(v.optional(), z.string()),
    createdAt: $makeSchema(
      ZovaRender.field('basic-date:formFieldDateRange'),
      v.filterTransform('a-web:dateRange'),
      v.optional(),
      z.string(),
    ),
  },
})
export class DtoStudentSelectReq extends $Dto.queryPage(EntityStudent, ['name', 'createdAt']) {}

搜索条件的 DTO 独立于列表数据。这里 name 是普通文本搜索,createdAt 渲染为日期范围选择器(formFieldDateRange),并通过 v.filterTransform 自动将前端选择的日期范围转换为后端查询格式。

3. 新建/编辑页

@Dto<IDtoOptionsStudentCreate>({
  blocks: [
    ZovaRender.block('basic-pageentry:blockPageEntry', {
      blocks: [
        ZovaRender.block('basic-pageentry:blockForm'),
        ZovaRender.block('basic-pageentry:blockToolbarRow', {
          actions: [
            ZovaRender.formActionRow('basic-form:actionSubmit', {
              permission: { actionInherit: 'update', formScene: ['create', 'edit'] },
            }),
            ZovaRender.formActionRow('basic-form:actionBack', { permission: { public: true } }),
          ],
        }),
      ],
    }),
  ],
})
export class DtoStudentCreate extends $Dto.create(() => ModelStudent) {}

新建页和编辑页结构相同:表单区 + 操作栏(提交/返回)$Dto.create` 和 `$Dto.update 分别继承自 Model ,自动带上 Entity 中定义的字段渲染配置。区别在于 formScene 控制提交按钮的权限——创建和编辑时显示,查看时隐藏。

4. 详情页

@Dto<IDtoOptionsStudentView>({
  blocks: [
    ZovaRender.block('basic-pageentry:blockPageEntry', {
      blocks: [
        ZovaRender.block('basic-pageentry:blockForm'),
        ZovaRender.block('basic-pageentry:blockToolbarRow', {
          actions: [
            ZovaRender.formActionRow('basic-form:actionBack', { permission: { public: true } }),
          ],
        }),
      ],
    }),
  ],
})
export class DtoStudentView extends $Dto.get(() => ModelStudent) {}

详情页只比编辑页少了一个提交按钮,继承自 $Dto.get,表单自动为只读模式。

总结一下 DTO 的页面组装模式:用 blocks 声明页面由哪些区域组成,用 actions 声明操作按钮,字段渲染则自动继承 Entity 的配置。整个过程不需要写 Vue 组件、不需要拼模板,一个 DTO 文件就是一个完整的 CRUD 页面。

五、与 Django Admin 对比:为什么值得换

特性 VonaJS Django Admin
后端技术栈 NodeJS + TypeScript Python + 服务端模板语言
前端技术栈 Vue3 + Vite8 + TypeScript HTML + CSS + JS
渲染机制 同构 SSR 服务端模板渲染
双层页签导航 支持 不支持
界面定制 自由定制,组件级可控 定制成本高,需覆盖模板
SSR 支持(含侧边栏、主题等) 不支持

Django Admin 的核心问题是:它用服务端模板渲染页面,前端技术栈停留在传统 HTML/CSS/JS 时代。想定制一个下拉框的样式、加一个自定义交互,就得去覆盖模板文件,维护成本随业务复杂度急剧上升。

VonaJS 采用前后端分离架构,前端是完整的 Vue3 应用,渲染配置通过 DTO 声明、组件按需替换,定制一个字段控件只需要换一个 ZovaRender.field() 的组件名。同时,Admin 中后台也支持 SSR ,刷新页面时侧边栏、多语言、主题等不会闪烁跳动。

六、与 NestJS 对比:DTO 不再只是校验

在 NestJS 中,DTO 的职责比较单一——参数校验。你需要用 class-validator 装饰器定义校验规则,再用 class-transformer 或手动方式生成 Swagger 文档。至于前端页面怎么渲染?那是另一个项目的事。

VonaJS 基于 Zod4 的统一 Schema ,让 DTO 同时承担四项职责:

  1. 参数校验:Zod 原生能力
  2. OpenAPI 文档:自动从 Schema 生成 Swagger 文档
  3. 渲染配置:通过 ZovaRender 声明字段在 Table/Form 中的渲染方式
  4. 数据序列化:Response 的脱敏处理、字段过滤

一份 Schema 定义,四处复用,从根源上消除了前后端字段定义不一致的问题。

七、总结

VonaJS 的核心价值很明确:用 DTO 驱动 CRUD 动态渲染,把中后台开发从"写页面"变成"配字段"。

过去你需要在 Entity 里定义字段、在 DTO 里定义校验、在前端组件里定义表格列和表单控件——三个地方维护同一组字段。现在,Entity 中一行 ZovaRender.cell()ZovaRender.field() 就能同时搞定显示和交互,DTO 中几个 block 就能组装出完整的页面结构。

如果你的项目是中后台系统、管理后台、数据驱动的 Web 应用,VonaJS 值得一试。

  • dto
  • 渲染
  • 声明式

    4 条回复    2026-06-10 10:52:34 +08:00

    indexError

    1

    indexError      5 月 21 日 via Android

    AI 想用哪个就用哪个

    zhennann

    2

    zhennann      5 月 21 日

    @indexError AI 确实能写任何框架的代码,但框架决定的是你让 AI 写多少。VonaJS 的 DTO 驱动渲染,一个文件配完增删改查,AI 少写 5 个页面组件,你也少审查 5 个页面组件。框架省的不是写代码的力气,是维护代码的成本。

    humbass

    3

    humbass      5 天前

    JS 写成这样也是够傻逼的,这种能流行就怪了。