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

推荐订阅源

Microsoft Security Blog
Microsoft Security Blog
S
Secure Thoughts
酷 壳 – CoolShell
酷 壳 – CoolShell
S
SegmentFault 最新的问题
WordPress大学
WordPress大学
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
美团技术团队
博客园 - 三生石上(FineUI控件)
Jina AI
Jina AI
V
Visual Studio Blog
腾讯CDC
小众软件
小众软件
有赞技术团队
有赞技术团队
博客园 - 聂微东
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
IT之家
IT之家
C
CERT Recently Published Vulnerability Notes
大猫的无限游戏
大猫的无限游戏
T
Threat Research - Cisco Blogs
SecWiki News
SecWiki News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Hacker News
The Hacker News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Troy Hunt's Blog
月光博客
月光博客
雷峰网
雷峰网
T
Tor Project blog
I
Intezer
S
Securelist
罗磊的独立博客
The Register - Security
The Register - Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
NISL@THU
NISL@THU
Google Online Security Blog
Google Online Security Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Cisco Talos Blog
Cisco Talos Blog
云风的 BLOG
云风的 BLOG
www.infosecurity-magazine.com
www.infosecurity-magazine.com
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
L
Lohrmann on Cybersecurity
The Cloudflare Blog
Attack and Defense Labs
Attack and Defense Labs
P
Privacy International News Feed
W
WeLiveSecurity
Forbes - Security
Forbes - Security
量子位
Last Week in AI
Last Week in AI

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
前端唯一的护城河?结合 AI 将字节组件库 Headless 化后的感想~
ai超级个体 · 2026-05-12 · via 博客园_首页

以下是我个人将团队正在使用的字节和蚂蚁的组件库 Arco/Ant Design, 结合 AI 将源码重写为无样式组件(Headless 化)的一些感触。简单结论是: AI 还是差点意思。

在目前的 Web 前端开发或独立开发圈子里,如果你还没听过 Headless(逻辑与 UI 解耦)组件库,那你可能正在错过 AI 时代最前沿的开发范式。

在海外,这类方案早已席卷社区。无论是大名鼎鼎的 MUI 旗下的 Base UI,还是狂揽 114k+ Star、彻底改变开发习惯的 shadcn/ui,都在传达一个信号:UI 的归 UI,逻辑的归逻辑。

💡 什么是 Headless? 简单来说,组件只负责驱动交互核心逻辑,但不强绑定任何视觉样式。开发者可以像捏泥人一样,在不破坏“大脑”的前提下,随心所欲地定制“皮肤”。

个人说明

首先说明一下,我自己潜心研究国内外组件库源码大概有两年时间了,例如国外的 chakra-ui, shadcn ui, floating-ui 组件库, 国内的 ant-design、字节的 arco-design, 腾讯的 Tdesign 组件库的源码。(偶尔发现源码 BUG 提的 PR 也合并了)。

也做过两个组件库的编写,改造过程中一个很深的感触就是 AI 从 0 到 1 造 Ant Design/Arco Design 级别的组件几乎不可行(你可以立马试试),甚至可以说是写的是垃圾代码, 必须有一个资深以上开发本身就了解这些组件的架构和核心技术细节的前提下,AI 才能在辅助层面极大的提高开发效率。

简单总结就是: AI 的产出代码质量好坏完全取决于开发者本身的技术水平。

虽然很多营销号宣称“AI 无敌”,只要几句话就能生成一切,但客观地说这些营销号的作者大多数并没有拿出很多自己开发经历的经验和开源出来的作品作为证据。

本文主要案例来自从源码层面改造字节的组件库 Arco Design 的表单组件 Form(改造了很多组件,这篇文中着重拿 Form 表单举例)。欢迎访问网站

(https://meow.frontlight.tech/zh/form) 查看改造后的 Form 组件具体功能和 API

好了,我们继续之前的 headless 的概念往下说。

现状:国内 B 端的“硬核”与 “枷锁”

虽然 Headless 组件库如此流行,但在国内 B 端市场,情况有些特殊:以 Ant Design 为代表的大厂系组件库(如字节的 Arco Design / Semi Design、腾讯的 TDesign 等)稳坐头把交椅。

坦白说,它们处理极其复杂业务场景的能力(联动、嵌套、动态校验等),确实远超国外的 Headless 库。这也是为什么国内开发者至今仍难以割舍这些组件库的原因。

但在全面拥抱 AI 编程的今天,这些传统的组件库无论是样式覆盖,还是通过修改 CSS 变量来定制样式,对 AI 来说都存在“适配性断层”——因为 AI 最擅长的是从零生成样式,而不是在已有的层层样式嵌套中搞魔改。

那么,一个硬核的问题摆在了我们面前:

我们能否既保留 Ant Design 极其强大的逻辑处理能力,又让它彻底“Headless 化” —— 也就是把 100% 的视觉控制权交还给开发者?让 DOM 结构的控制权交给开发者?

答案是肯定的,但问题就在于改造的难度非常大。为什么呢,我们来举一个实战翻车案例:

案例分析:一个“暗藏杀机”的 validate 需求

我举个典型的例子。在重构 Form 组件的校验方法时,我们需要一个支持多种调用方式的 validate 函数:

  • 回调函数模式:this.form.validate((errors, values) => { ... })
  • Promise 模式:this.form.validate().then(values => { ... })
  • Async/Await 模式:const values = await this.form.validate()

为了实现这个功能,在 Arco Design 源码中是这样做的,首先它用一个高阶函数 promisify 来对原始校验逻辑进行包装。

// 核心逻辑简述
public validate = promisify((...args) => {
// 省略复杂的校验逻辑...
const promises = controlItems.map(x => x.validateField());

Promise.all(promises).then(result => {
    // 逻辑处理...
    if (Object.keys(errors).length) {
      callback?.(errors, cloneDeep(values));
    } else {
      callback?.(null, cloneDeep(values));
    }
  });
});

而 promisify 的内部实现,最关键的一行是判断用户当前的调用意图的 if 语句:

function promisify(fn) {
  return function (...args) {
      // 关键判断:如果最后一个参数是函数,说明用户在用回调模式
      if (typeof args[args.length - 1] === 'function') {
        return fn.apply(this, args); // 直接执行,不返回 Promise
      } else {
        // 否则,返回一个全新的 Promise 实例
        return new Promise((resolve, reject) => {
          // ...将原始函数 Promise 化
        });
      }
    };
}

而在重构这部分代码的时候,我希望优化 this.validate 内部逻辑,并去掉 promisify, 用来减少代码量,但遗憾的是这么一个小小的需求, AI 会在这里“集体翻车”。

我测试了目前市面上最顶尖的模型:Claude 4.7 (Opus), GPT-5.4, Gemini 3.1 Pro。

在我没有明确提示的情况下,没有一个模型能自动识别并改造出兼容 this.validate 多种用法的代码。它们要么粗暴地全部转化为 Promise,要么干脆破坏了原有的回调链路。(核心是一定要判断 this.validate() 中最后一个参数是否是回调,如果是就不要包装为 Promise, 否则只保留之前版本回调的用法)。

只有在我明确指出错误, 并且加入限定的提示词后,它们才会像“挤牙膏”一样修正。

很多类似体验让我深切感受到之前提到的结论: 如果使用者本身技术不够扎实,在面对这种复杂基础设施代码时,AI 写出的代码,很难察觉到它异常的地方。

同时对我们打工人来说有个非常矛盾的使用 AI 的问题浮现出来:

对于我们打工人来说,AI 犯错,顶多是生成了一段 Bad Code;但人犯错,可能你人就被裁了或者影响你的绩效。

传统组件库在 AI 时代的局限性

说到这里,可能有同学可能会问:传统的组件库如 Ant DesignAI 也能帮我改样式啊,为什么非要搞 Headless

这里我们要纠正一个认知偏差:AI 最强大的能力是“生成”,而它最痛苦的能力是“魔改”。

Ant Design 目前的 UI 方案对 AI 并不友好

目前国内类似 Antd 的组件库仍然是以改变 CSS 变量的方式定制 UI, 这就带来两个核心问题。

  • 一个组件能定义的 CSS 变量有限,并不能随心所以修改 UI
  • 另一个是组件内部 DOM 结构固定,外部是无法修改的 这就导致 AI 并不知道 Antd 组件源码的 DOM 结构。并且修改的 CSS 变量也是基于语义化,而不是真的知道源码 DOM 结构上下文而做出精确修改。

所以传统组件库像是一个高度集成的“黑盒”。这是对 AI 非常非常不友好的。

而在 Headless 模式下,所有的 DOM 结构都是你(或者 AI)亲手写出来的。 AI 对它生成的代码拥有 100% 的上下文掌控力。它不需要去猜测黑盒里的逻辑,它只需要根据你的描述,在逻辑钩子外结合任何 CSS 框架,例如 Tailwind CSSUnoCSS, Sass 等等。

现实中类似的样式历史债务比比皆是

我相信大多数做过复杂 B 端项目的团队都遇到过一个问题,就是 UI 有自己定制化的样式,然后你不得不去覆盖 Ant Design 的样式, 我直接给你看飞书文档中(他们前期部分团队应该用的 Ant Design),这样国内的顶级团队依然有这样的问题。

以下是飞书问卷页面,我随手截图的内容,这种覆盖的样式如下:

image

而这只是一个按钮的 CSS 样式,团队自定义后的样式覆盖情况(覆盖了起码有 4 次)。连字节核心产品都这样,你就可想而知这是一个多么普遍的问题了。

核心问题就在于多团队合作的时候,在这种提供完整 UI 的组件库里是很难避免样式覆盖问题的(以为 CSS 类已经写死在组件里了)。即使现在使用 CSS 变量来控制 UI 样式。

Headless 组件的优势举例

我相信绝大多数前端有过后台管理系统的经验,都会用过 Ant Design 的 Form 组件,其中 Ant Design 的 Form 组件在布局中最大的问题在于,其本身将固定的栅格布局系统嵌入其中,代码如下(注意 labelCol={{ span: 8 }}wrapperCol={{ span: 16 }})

import React from 'react';
import { Button, Form, Input } from 'antd';

const App = () => (
  <Form
    name="basic"
    labelCol={{ span: 8 }}
    wrapperCol={{ span: 16 }}
    onFinish={(values) => console.log('Success:', values)}
  >
    <Form.Item
      label="Username"
      name="username"
      rules={[{ required: true, message: 'Please input your username!' }]}
    >
      <Input />
    </Form.Item>

    <Form.Item label={null}>
      <Button type="primary" htmlType="submit">Submit</Button>
    </Form.Item>
  </Form>
);

大家看上面的代码,核心就在于 labelCol={{ span: 8 }}wrapperCol={{ span: 16 }}。这看似简单的配置,其实是开发者噩梦的开始:

  • Ant Design 的栅格是基于将屏幕划分为 24 份。所以 span: 8,意味着是整个当前宽度的 8 / 24
  • 但我们开发中,几乎没有遇到 UI 是根据这种栅格布局给我们开发设计稿的,基本都是 px 为单位的设计稿,或者响应式布局的设计稿。

这就造成了如果我们想精确到例如 Form 表单中,Label 和具体的例如 Input 左右间距 24px 是比较麻烦的。

再例如如果你想在 LabelInput 之间插入一个自定义图标,或者想改变 LabelHTML 标签属性。对不起,如果组件库没给 API,你进不去。这种“黑盒”结构导致你即便有再强的 CSS、Html 功底,也只能在外面干瞪眼。

但即使 Headless 组件这么好用,为什么国内复杂的业务前端开发基本还是离不开 Ant Design,Arco Design 这类组件库呢?

为什么 shadcn/ui(headless 组件库) 很香,但国内 B 端却离不开 Ant Design?

最近两年,shadcn/ui 在开发者圈子里简直是“神”一般的存在。它那种把代码直接复制到项目里的逻辑,给了开发者前所未有的掌控感。但如果你拿着 shadcn/ui 去接一个国内大厂的 B 端外包或者内部系统,大概率你会写代码写到崩溃。

为什么?因为国内 B 端业务的复杂度,和国外那种追求极简的 SaaS 工具完全不是一个量级的。

“功能密度”的降维打击

举个例子,Select 组件,我除去单纯样式相关的参数,Ant Design 提供约 40+ 个左右的功能参数,而 shadcn/ui 除去单纯样式相关的参数,提供约 10+ 个左右的功能参数。

而这其中的功能参数的差值,使用者想要实现一样的功能,不仅仅要写 UI 样式,还需要补齐对应的逻辑功能。

所以也就出现了一些基于这些 headless 组件库封装的上层组件库(感觉他们的意义不大,还不如直接用 Arco Design, Ant Design 呢)。

“业务确定性”胜过“样式自由”

国内的研发节奏只有两个字:。之前在滴滴做研发的时候,当时是做一个对外售卖的财务软件,就那种按分钟计时的排期(时间紧),你又害怕出 bug 直接影响绩效(质量要求高,功能要求多)。当时唯一的选择就是 Arco DesignAnt Design 这种大而全的组件库。

你说样式自定义的问题?那就随缘处理了,反正强行样式覆盖嘛😭。

所以,最完美的解法是:抽离 Arco/Antd 历经千万级 DAU 业务考验的内部逻辑,套上类似 shadcn/ui 的极致自由度。 我们来看看改造后的 Form 具体长什么样:

我们拿之前的 Arco Design 和 Ant Design 的 Form 使用案例来说,只是改造之前的用法:

import React from 'react';
import { Button, Form, Input } from 'antd';

const App = () => (
  <Form
    name="basic"
    labelCol={{ span: 8 }}
    wrapperCol={{ span: 16 }}
    onFinish={(values) => console.log('Success:', values)}
  >
    <Form.Item
      label="Username"
      name="username"
      rules={[{ required: true, message: 'Please input your username!' }]}
    >
      <Input />
    </Form.Item>

    <Form.Item label={null}>
      <Button type="primary" htmlType="submit">Submit</Button>
    </Form.Item>
  </Form>
);

改造后的用法如下, 使用方法跟 React-Form-Hook 非常类似:

import { Form } from'@meow-kit/web-react';
import { Button, Checkbox, Input } from'antd';

function App() {
const [form] = Form.useForm();
const errors = Form.useFormErrors(form);

const onFinish = (values) => {
    console.log('Success:', values);
  };

const onFinishFailed = (errorInfo) => {
    console.log('Failed:', errorInfo);
  };

return (
    <Form
      name="basic"
      form={form}
      initialValues={{ remember: true }}
      onSubmit={onFinish}
      onSubmitFailed={onFinishFailed}
      autoComplete="off"
      className="w-full"
    >
      <FormControl
        label="Username"
        field="username"
        errors={errors}
        getValueFromEvent={(e) => e.target.value}
        rules={[{ required: true, message: 'Please input your username!' }]}
      >
        <Input className="w-full" placeholder="Enter your username" />
      </FormControl>

      <Button type="primary" htmlType="submit" className="ml-22">
        Submit
      </Button>
    </Form>
  );
};

const FormControl = ({
  label,
  field,
  rules,
  errors,
  children,
  className = '',
  layout = 'horizontal', // 'vertical' | 'horizontal' 
  labelWidth = 'w-18',
  ...itemProps
}) => {
// get current field error message
const errorMessage = errors?.[field]?.message;
// check if field is required, used to render red star icon
const isRequired = rules?.some((rule) => rule.required);

const isHorizontal = layout === 'horizontal';

return (
    <div
      className={`flex relative ${
        isHorizontal ? 'flex-row items-start' : 'flex-col'
      } ${className}`}
    >
      {/* Label 区域 */}
      {label && (
        <label
          className={`
            text-sm font-medium text-gray-700
            ${
              isHorizontal
                ? `shrink-0 text-right mr-4 pt-1.5 ${labelWidth}` // 横向:固定宽度、靠右对齐、向下偏移以对齐输入框内文字
                : 'mb-1.5 block' // 纵向:底部留白、独占一行
            }
          `}
        >
          {isRequired && <span className="mr-1 text-red-500">*</span>}
          {label}
        </label>
      )}

      {/* Core control wrapper area: occupy remaining space */}
      <div className="flex-1 min-w-0 flex flex-col">
        <Form.Item field={field} rules={rules} {...itemProps}>
          {children}
        </Form.Item>

        {/* Unified Error message UI */}
        <div
          className={`mt-1 min-h-[20px] text-xs text-red-500 transition-all duration-300 ${
            errorMessage
              ? 'translate-y-0 opacity-100'
              : '-translate-y-1 opacity-0'
          }`}
        >
          {errorMessage}
        </div>
      </div>
    </div>
  );
};

其中 FormControl 组件是豆包 AI 写的(证明语义化后,简单的模型也能很好的完成任务),是我们封装的 UI样式,这个是交给用户的(我把Form` 中所有跟样式有关的源码全部抽离),然后使用 AI 来自定义封装的,基本上可以任意让 AI 折腾,DOM 结构和 CSS 随便自定义。

所以这是一个很好的将 Ant Design/Arco Design Headless 化的案例。欢迎大家一起讨论~

源码地址:github仓库(https://github.com/lio-mengxiang/meow-ui)