2. 以 Bubble 为例,看 Ant Design X Vue 组件是怎么设计的

以 Bubble 为例,看 Ant Design X Vue 组件是怎么设计的

这篇文章以 Bubble 为例,简单看一下它在 Ant Design X Vue 里是怎么设计的,以及如果我们后续要改这个组件,应该从哪些文件入手。

Ant Design X Vue 里,Bubble 是一个很核心的组件。

它承担的是 AI 对话场景里最基础、也最常见的能力:消息展示

看起来它只是一个聊天气泡,但实际设计里包含了很多 AI 产品需要的细节,比如:

text 复制代码
头像
消息位置
加载态
打字效果
消息头部和底部
自定义消息内容
消息列表
自动滚动
角色默认配置
语义化 class 和 style
主题 token

Bubble 解决什么问题?

在 AI Chat / Agent 产品里,消息展示不是简单渲染一段文本。

一条消息通常会有:

text 复制代码
发送方
头像
消息内容
消息方向
加载状态
打字动画
Markdown / 自定义内容
操作区域
状态信息

如果是消息列表,还需要考虑:

text 复制代码
多条消息的渲染
用户消息和 AI 消息的默认样式
新消息出现时自动滚动到底部
流式输出时持续滚动
用户手动滚动后暂停自动滚动
每条消息打字结束后再展示下一条

所以 Bubble 不是一个单纯的 UI 元素,而是 AI 对话界面里的基础消息单元。

相关文件在哪里?

Bubble 相关源码主要在:

text 复制代码
src/bubble/

目录结构大致如下:

可以先按职责理解:

文件 作用
Bubble.vue 单条消息气泡组件
BubbleList.vue 消息列表组件,也就是 Bubble.List
interface.ts 类型定义,包含 props、ref、roles 等
index.ts 组件导出和 install 注册
loading.vue 默认 loading 效果
context.ts 列表和单条气泡之间的上下文通信
hooks/useTypingConfig.ts 解析 typing 配置
hooks/useTypedEffect.ts 实现打字效果
hooks/useListData.ts 合并消息数据和角色配置
hooks/useDisplayData.ts 控制列表中消息的逐条展示
style/index.ts Bubble 主样式
style/content.ts variant 和 shape 样式
style/list.ts Bubble.List 样式

如果是第一次看,建议顺序是:

text 复制代码
1. interface.ts
2. index.ts
3. Bubble.vue
4. BubbleList.vue
5. hooks/
6. style/
7. docs/component/bubble.md
8. docs/examples/bubble/

对外导出是怎么设计的?

入口文件是:

text 复制代码
src/bubble/index.ts

这里做了几件事:

ts 复制代码
import BubbleComponent from './Bubble.vue';
import BubbleList from './BubbleList.vue';

const Bubble = Object.assign(BubbleComponent, {
  List: BubbleList,
});

也就是说,对外使用时,既可以用单个气泡:

vue 复制代码
<Bubble content="hello" />

也可以用列表:

vue 复制代码
<Bubble.List :items="messages" />

这种设计比较常见:把子组件挂到主组件上

在 API 语义上,Bubble.List 表示它是 Bubble 体系下的列表能力,而不是另一个完全独立的组件。

同时 index.ts 里还会注册组件:

ts 复制代码
Bubble.install = function(app: App) {
  app.component(Bubble.name, Bubble);
  app.component(BubbleList.name, BubbleList);
  return app;
}

这样既支持按需引入,也支持通过插件方式注册。

类型是怎么设计的?

类型主要在:

text 复制代码
src/bubble/interface.ts

核心类型有几个:

text 复制代码
BubbleProps
BubbleListProps
BubbleDataType
RolesType
TypingOption
BubbleRef
BubbleListRef

BubbleProps

BubbleProps 是单条气泡的 props。

它里面比较重要的字段有:

ts 复制代码
avatar?: Partial<_AvatarProps> | VNode | (() => VNode);
placement?: 'start' | 'end';
loading?: boolean;
typing?: TypingOption | boolean;
content?: ContentType;
messageRender?: (content: ContentType) => VNode | string;
loadingRender?: () => VNode;
variant?: 'filled' | 'borderless' | 'outlined' | 'shadow';
shape?: 'round' | 'corner';
header?: VNode | string | ((content, info) => VNode | string);
footer?: VNode | string | ((content, info) => VNode | string);

这几个字段基本覆盖了单条消息展示的核心能力:

text 复制代码
avatar          头像
placement       消息位置
loading         加载中
typing          打字效果
content         消息内容
messageRender   自定义消息渲染
loadingRender   自定义 loading
variant         气泡样式变体
shape           气泡形状
header/footer   头部和底部区域

BubbleListProps

BubbleListProps 是消息列表的 props。

主要字段有:

ts 复制代码
items?: BubbleDataType[];
autoScroll?: boolean;
roles?: RolesType;
onScroll?: (e: Event) => void;

这里有两个重点。

第一个是 items,它表示消息列表数据。

第二个是 roles,它可以给不同角色配置默认气泡属性。

比如:

ts 复制代码
const roles = {
  user: {
    placement: 'end',
  },
  assistant: {
    placement: 'start',
    typing: true,
  },
};

这样 items 里只要写:

ts 复制代码
{
  role: 'assistant',
  content: '你好,我是 AI 助手'
}

组件就可以根据 role 自动套用默认样式。

这个设计很适合 AI 对话,因为消息通常天然分角色:

text 复制代码
user
assistant
system
tool

Bubble.vue:单条气泡怎么设计?

核心文件是:

text 复制代码
src/bubble/Bubble.vue

这个组件使用的是:

vue 复制代码
<script setup lang="tsx">

也就是说,它不是传统 template 写法,而是 Vue + TSX 写法。

这么写的好处是:

text 复制代码
更方便处理动态插槽
更方便组合 VNode
更接近 Ant Design React 版本的组件结构
复杂渲染逻辑更集中

Bubble.vue 里大致可以拆成几块:

text 复制代码
props 和 slots
content 本地状态
XProvider 全局配置
组件级配置
打字效果
样式 class 合并
头像渲染
内容渲染
header/footer 渲染
最终 render
ref 暴露

props 和 slots

Bubble.vue 通过 defineProps 拿到所有配置:

ts 复制代码
const {
  prefixCls: customizePrefixCls,
  rootClassName,
  classNames = {},
  styles = {},
  avatar,
  placement = 'start',
  loading = false,
  loadingRender,
  typing,
  content: contentProp = '',
  messageRender,
  variant = 'filled',
  shape,
  onTypingComplete,
  header,
  footer,
  _key,
  ...otherHtmlProps
} = defineProps<BubbleProps<T>>();

这里能看到一些默认值:

text 复制代码
placement 默认 start
loading 默认 false
content 默认空字符串
variant 默认 filled
classNames 默认空对象
styles 默认空对象

组件还定义了多个插槽:

text 复制代码
avatar
header
footer
loading
message

这些插槽的设计目的,是给业务方留扩展空间。

比如:

vue 复制代码
<Bubble content="hello">
  <template #avatar>
    <UserAvatar />
  </template>

  <template #message="{ content }">
    <MarkdownRenderer :content="content" />
  </template>
</Bubble>

所以 Bubble 的设计不是把所有展示都写死,而是提供默认 UI,同时允许业务覆盖关键区域。

prefixCls:样式类名前缀

组件里有一段:

ts 复制代码
const { direction, getPrefixCls } = useXProviderContext();
const prefixCls = getPrefixCls('bubble', customizePrefixCls);

这里是为了统一 class 命名。

默认情况下,气泡组件可能会生成类似:

text 复制代码
ant-bubble
ant-bubble-start
ant-bubble-content

实际前缀取决于 XProvider 的配置。

这样做的好处是:

text 复制代码
统一组件库 class 前缀
支持自定义 prefixCls
支持 RTL 方向
方便主题和样式隔离

打字效果是怎么实现的?

Bubble 的打字效果主要由两个 hook 完成:

text 复制代码
hooks/useTypingConfig.ts
hooks/useTypedEffect.ts

Bubble.vue 里是这样使用的:

ts 复制代码
const [typingEnabled, typingStep, typingInterval, typingSuffix] = useTypingConfig(() => typing);

const [typedContent, isTyping] = useTypedEffect(
  content,
  typingEnabled,
  typingStep,
  typingInterval,
);

useTypingConfig

useTypingConfig 负责把 typing 参数转换成统一配置。

typing 可以是:

ts 复制代码
true

也可以是:

ts 复制代码
{
  step: 2,
  interval: 30,
  suffix: '▋'
}

默认配置是:

ts 复制代码
{
  step: 1,
  interval: 50,
  suffix: null,
}

也就是说:

text 复制代码
step      每次输出几个字符
interval  每次输出间隔多少毫秒
suffix    打字后缀

如果后续想改默认打字速度,就改这里。

useTypedEffect

useTypedEffect 负责真正的打字逻辑。

它会维护:

text 复制代码
prevContent    上一次内容
typingIndex    当前展示到第几个字符
typedContent   当前实际展示内容
isTyping       是否正在打字

核心思路是:

text 复制代码
如果 typing 没开启,直接展示完整 content
如果 typing 开启,并且 content 是字符串,就按 typingIndex 截断展示
通过 setTimeout 定时增加 typingIndex
直到 typingIndex 到达 content.length

还有一个细节:它会比较新旧内容的公共前缀。

这是为了适配流式输出场景。

比如 AI 返回内容是逐步追加的:

text 复制代码
你
你好
你好,我
你好,我是

这种情况下,新内容通常是旧内容的延长,组件可以从差异位置继续打字,而不是每次都从头开始。

这对 AI streaming 输出很重要。

loading 是怎么处理的?

Bubble.vue 里有一个 contentNode

ts 复制代码
const contentNode = computed<VNode>(() => {
  if (loading) {
    if (slots.loading) {
      return slots.loading();
    }
    return loadingRender ? loadingRender() : <Loading prefixCls={prefixCls} />;
  } else {
    return (
      <>
        {mergedContent.value}
        {isTyping.value && toValue(typingSuffix)}
      </>
    );
  }
});

这里的逻辑很清楚:

text 复制代码
如果 loading 为 true:
  优先使用 loading 插槽
  其次使用 loadingRender
  最后使用默认 Loading 组件

如果 loading 为 false:
  展示消息内容
  如果正在打字,展示 typing suffix

默认 loading 组件在:

text 复制代码
src/bubble/loading.vue

如果要改默认 loading 样式,可以看:

text 复制代码
src/bubble/loading.vue
src/bubble/style/index.ts

内容渲染优先级

消息内容的渲染逻辑在 mergedContent 里:

ts 复制代码
const mergedContent = computed(() => {
  if (slots.message) {
    return slots.message({ content: typedContent.value as any });
  }
  return messageRender ? messageRender(typedContent.value as any) : typedContent.value
});

优先级是:

text 复制代码
message 插槽
messageRender
默认 typedContent

也就是说,如果业务方传了 message 插槽,就完全接管消息内容渲染。

如果没有插槽,但传了 messageRender,就走函数渲染。

都没有,则直接渲染 content

这个设计适合支持 Markdown、代码块、图表等复杂消息内容。

比如要支持 Markdown,可以通过:

vue 复制代码
<Bubble :content="markdownText">
  <template #message="{ content }">
    <MarkdownRenderer :content="content" />
  </template>
</Bubble>

或者使用 messageRender

Bubble 支持头部和底部区域:

text 复制代码
header
footer

它们既可以是静态内容,也可以是函数:

ts 复制代码
header?: VNode | string | ((content, info) => VNode | string);
footer?: VNode | string | ((content, info) => VNode | string);

也可以通过插槽传入:

vue 复制代码
<Bubble content="hello">
  <template #header="{ content, info }">
    AI 助手
  </template>

  <template #footer="{ content, info }">
    <span>刚刚</span>
  </template>
</Bubble>

适合放这些内容:

text 复制代码
消息发送人
时间
模型名称
引用来源
复制 / 重试 / 点赞按钮
状态提示

如果要给每条消息底部加操作按钮,优先考虑 footerActions 组件组合。

classNames 和 styles:语义化自定义

Bubble 支持:

ts 复制代码
classNames?: Partial<Record<SemanticType, string>>;
styles?: Partial<Record<SemanticType, CSSProperties>>;

其中 SemanticType 是:

ts 复制代码
type SemanticType = 'avatar' | 'content' | 'header' | 'footer';

这意味着业务方可以针对某个语义区域定制样式,而不是硬写深层 CSS 选择器。

比如:

vue 复制代码
<Bubble
  content="hello"
  :classNames="{ content: 'my-bubble-content' }"
  :styles="{ content: { background: '#f6ffed' } }"
/>

这种设计对于组件库很重要。

因为组件库需要同时满足:

text 复制代码
默认样式可用
局部样式可覆盖
不要暴露太多内部 DOM 细节

Bubble.List:消息列表怎么设计?

Bubble.List 的源码在:

text 复制代码
src/bubble/BubbleList.vue

它主要做几件事:

text 复制代码
接收 items
合并 roles 默认配置
控制实际展示的 displayData
渲染多个 Bubble
处理自动滚动
把消息更新事件传给列表
暴露 scrollTo 方法

可以简单理解为:

text 复制代码
Bubble 负责一条消息
Bubble.List 负责一组消息

roles 是怎么合并的?

Bubble.List 会调用:

text 复制代码
hooks/useListData.ts

这个 hook 负责把 itemsroles 合并。

核心逻辑类似:

ts 复制代码
return {
  ...getRoleBubbleProps(bubbleData, i),
  ...bubbleData,
  key: mergedKey,
};

注意这里的顺序:

text 复制代码
先展开 role 默认配置
再展开当前 bubbleData

所以单条消息上的配置优先级更高。

比如:

ts 复制代码
const roles = {
  assistant: {
    placement: 'start',
    typing: true,
  },
};

const items = [
  {
    role: 'assistant',
    content: 'hello',
    typing: false,
  },
];

最终 typing 会是 false,因为 items 里的配置覆盖了 roles

这符合直觉:角色给默认值,单条消息可以单独覆盖。

displayData:为什么不是直接渲染所有 items?

Bubble.List 没有直接渲染 items,而是经过:

text 复制代码
hooks/useDisplayData.ts

这个 hook 会返回:

ts 复制代码
const [displayData, onTypingComplete] = useDisplayData(mergedData);

它的作用是控制消息逐条展示。

简单说:

text 复制代码
先展示当前应该展示的消息
如果最后一条消息打字完成
再继续展示下一条

这样做的原因是,AI 对话里可能会出现多条消息连续进入列表。

如果都同时打字,会显得很乱。

通过 displayData,可以让消息按顺序出现,体验更自然。

自动滚动是怎么做的?

Bubble.List 里有几个状态:

text 复制代码
listRef          列表 DOM
bubbleRefs       每条 Bubble 的实例引用
scrollReachEnd   当前是否滚动到底部
updateCount      内容更新次数

自动滚动的大致逻辑是:

text 复制代码
如果 autoScroll 开启
并且用户当前在底部
当消息内容变化时
列表滚动到底部

其中,单条 Bubble 在内容更新时会触发:

ts 复制代码
onUpdate?.();

这个事件通过 context.ts 传给 Bubble.List

列表收到后会更新 updateCount,再触发滚动逻辑。

这里的设计重点是:流式输出时,内容不是新增消息,而是同一条消息持续变长

如果只监听 items.length,流式输出时不会自动滚动。

所以它需要让 BubbletypedContent 变化时通知列表:

text 复制代码
我这条消息内容更新了,你可以尝试滚动一下

这就是 context.ts 的作用。

scrollTo 方法

Bubble.List 通过 defineExpose 暴露了:

ts 复制代码
scrollTo({
  key,
  offset,
  behavior,
  block,
})

支持两种滚动方式:

text 复制代码
按 offset 滚动
按消息 key 滚动

比如可以滚动到某条消息:

ts 复制代码
bubbleListRef.value?.scrollTo({
  key: 'message_1',
  behavior: 'smooth',
});

如果后续要做"跳转到引用消息""定位错误消息""定位工具调用结果",这个能力就很有用。

样式是怎么设计的?

Bubble 的样式在:

text 复制代码
src/bubble/style/

主要文件有:

text 复制代码
style/index.ts
style/content.ts
style/list.ts

style/index.ts

style/index.ts 负责基础样式,比如:

text 复制代码
整体 flex 布局
start / end 方向
RTL
头像布局
header / footer
content wrapper
loading 动画
打字光标动画

这里定义了两个关键动画:

text 复制代码
loadingMove
cursorBlink

loadingMove 用于 loading 点点动画。

cursorBlink 用于打字时的光标闪烁。

如果要改默认光标样式,可以看这里:

ts 复制代码
[`&${componentCls}-typing ${componentCls}-content:last-child::after`]

style/content.ts

content.ts 负责内容样式变体。

主要有两类:

text 复制代码
variant
shape

variant 包括:

text 复制代码
filled
borderless
outlined
shadow

shape 包括:

text 复制代码
round
corner

所以如果要新增一个气泡样式,比如:

text 复制代码
variant = 'soft'

就需要改:

text 复制代码
src/bubble/interface.ts
src/bubble/style/content.ts
docs/component/bubble.md
docs/examples/bubble/

style/list.ts

list.ts 负责列表样式,比如:

text 复制代码
flex column
gap
overflowY
滚动条样式

如果要改消息间距、滚动条样式、列表布局,主要看这里。

如果我们要改 Bubble,应该怎么改?

下面按常见需求来讲。

场景一:新增一个 prop

比如要给 Bubble 新增一个 status,表示消息状态:

text 复制代码
sending
success
error

建议修改顺序:

text 复制代码
1. src/bubble/interface.ts
2. src/bubble/Bubble.vue
3. src/bubble/style/index.ts 或 content.ts
4. docs/component/bubble.md
5. docs/examples/bubble/ 新增示例
6. play/src/App.vue 本地验证

第一步,在 BubbleProps 中新增类型:

ts 复制代码
status?: 'sending' | 'success' | 'error';

第二步,在 Bubble.vuedefineProps 里取出:

ts 复制代码
status,

第三步,根据 status 加 class:

ts 复制代码
{
  [`${prefixCls}-status-${status}`]: status,
}

第四步,在样式文件里写对应样式。

第五步,更新文档 API 表格和示例。

这样改比较完整,不容易出现"组件能用但文档没有"的问题。

场景二:新增一种 variant

比如要新增:

text 复制代码
variant = "soft"

需要改类型:

text 复制代码
src/bubble/interface.ts

把:

ts 复制代码
variant?: 'filled' | 'borderless' | 'outlined' | 'shadow';

改成:

ts 复制代码
variant?: 'filled' | 'borderless' | 'outlined' | 'shadow' | 'soft';

然后改样式:

text 复制代码
src/bubble/style/content.ts

新增:

ts 复制代码
'&-soft': {
  backgroundColor: token.colorFillSecondary,
  border: `1px solid ${token.colorBorderSecondary}`,
},

最后更新:

text 复制代码
docs/component/bubble.md
docs/examples/bubble/variant.vue

如果要本地快速验证,可以先改:

text 复制代码
play/src/App.vue

写一个:

vue 复制代码
<Bubble content="soft bubble" variant="soft" />

场景三:修改打字效果

打字效果主要看:

text 复制代码
src/bubble/hooks/useTypingConfig.ts
src/bubble/hooks/useTypedEffect.ts
src/bubble/style/index.ts

如果只是改默认速度,改:

text 复制代码
useTypingConfig.ts

比如:

ts 复制代码
const baseConfig = {
  step: 1,
  interval: 50,
  suffix: null,
};

如果要改"如何逐字输出",改:

text 复制代码
useTypedEffect.ts

如果要改打字光标样式,改:

text 复制代码
style/index.ts

比如当前光标是通过 CSS 伪元素实现的:

text 复制代码
content: "|"
animation: cursorBlink

如果想换成别的样式,可以在这里改。

场景四:修改 loading 效果

默认 loading 组件在:

text 复制代码
src/bubble/loading.vue

loading 动画样式在:

text 复制代码
src/bubble/style/index.ts

如果业务方只是临时自定义,不需要改源码,可以直接用:

vue 复制代码
<Bubble loading>
  <template #loading>
    <MyLoading />
  </template>
</Bubble>

或者:

vue 复制代码
<Bubble :loadingRender="renderLoading" />

如果要改组件库默认 loading,再改 loading.vue 和样式文件。

场景五:修改自动滚动逻辑

自动滚动主要在:

text 复制代码
src/bubble/BubbleList.vue

相关逻辑包括:

text 复制代码
scrollReachEnd
updateCount
onInternalScroll
watch updateCount/listRef/scrollReachEnd
watch displayData.length
onBubbleUpdate

如果要改"什么时候滚到底部",主要看这些 watcher。

如果要改"滚动到底部的行为",看:

ts 复制代码
unref(listRef).scrollTo({
  top: unref(listRef).scrollHeight,
});

比如想改成平滑滚动,可以加:

ts 复制代码
behavior: 'smooth'

但这里要谨慎。

流式输出时内容会频繁变化,如果每次都 smooth,可能导致滚动表现拖沓。所以这类修改最好在真实流式输出场景里测试。

场景六:修改消息列表的数据合并逻辑

如果要改 rolesitems 的合并规则,看:

text 复制代码
src/bubble/hooks/useListData.ts

当前顺序是:

ts 复制代码
{
  ...roleDefaultProps,
  ...bubbleData,
}

也就是说:

text 复制代码
role 提供默认值
单条消息可以覆盖 role

这个顺序一般是合理的。

如果要改变优先级,需要非常谨慎,因为这会影响用户对 API 的直觉。

场景七:支持新的消息内容类型

当前 BubbleContentType 是:

ts 复制代码
VNode | string | AnyObject | number

如果要支持更复杂的结构化消息,比如:

ts 复制代码
type MessageContent = {
  type: 'text' | 'image' | 'tool';
  value: unknown;
}

可以有两种方式。

第一种,不改组件源码,业务层通过 messageRendermessage 插槽处理。

这是更推荐的方式。

vue 复制代码
<Bubble :content="message">
  <template #message="{ content }">
    <MessageRenderer :content="content" />
  </template>
</Bubble>

第二种,改 BubbleContentType 和默认渲染逻辑。

这种改动影响面更大,只有当组件库想原生支持某类消息时才建议这么做。

场景八:新增文档示例

组件改完之后,最好补文档。

Bubble 文档在:

text 复制代码
docs/component/bubble.md

示例在:

text 复制代码
docs/examples/bubble/
docs/examples-setup/bubble/

如果新增一个示例,比如:

text 复制代码
docs/examples/bubble/status.vue

还需要在 docs/component/bubble.md 里加:

md 复制代码
### 消息状态

:::demo 展示不同消息状态。

bubble/status

:::

这样文档站才能展示。

本地怎么验证?

最简单的方式是改:

text 复制代码
play/src/App.vue

然后启动:

bash 复制代码
pnpm play

如果要看文档效果,启动:

bash 复制代码
pnpm docs:dev

如果要做构建验证,可以跑:

bash 复制代码
pnpm --dir play run build

或者完整构建:

bash 复制代码
pnpm build

如果只是改文档,则可以跑:

bash 复制代码
pnpm docs:build

一个推荐的修改流程

如果以后真的要改 Bubble,建议按这个流程来:

text 复制代码
1. 先明确需求属于 API、样式、行为、文档还是示例
2. 先看 interface.ts,确认类型怎么扩展
3. 再看 Bubble.vue 或 BubbleList.vue,确认实现位置
4. 如果涉及打字、列表、滚动,再看 hooks
5. 如果涉及视觉,再看 style/
6. 在 play/src/App.vue 写最小 demo
7. 跑 pnpm --dir play run build
8. 更新 docs/component/bubble.md
9. 更新 docs/examples/bubble/

这个流程可以避免只改实现、不改类型,或者只改组件、不改文档的问题。

小结

BubbleAnt Design X Vue 里非常典型的组件。

它的设计思路可以概括成:

text 复制代码
Bubble.vue 负责单条消息渲染
BubbleList.vue 负责消息列表、自动滚动和顺序展示
interface.ts 负责 API 类型
hooks/ 负责复杂行为拆分
style/ 负责样式和主题 token
docs/examples 负责用户使用示例

这种拆分方式比较适合组件库开发。

因为组件库不仅要实现功能,还要考虑:

text 复制代码
API 是否清晰
类型是否完整
默认样式是否可用
业务是否能自定义
文档是否跟得上
示例是否能覆盖常见场景
相关推荐
Liora_Yvonne1 小时前
Monorepo 里的 shared、services 和 ui 到底怎么划分?
前端
PedroQue991 小时前
uni-router v2.1.0 升级:导航守卫全面支持返回值模式
前端·uni-app
猫七先生1 小时前
从零到一:个人博客自动化部署踩坑全记录
前端
淼澄研学1 小时前
Python调用通义千问API实现长尾搜题内容自动化生成实战
前端·react.js·架构
绿岛之北1 小时前
Electron 安全入门:为什么一个 XSS 可能变成 RCE?
前端·electron
舒灿1 小时前
DeepSeek Harness——Agent自我进化的实现途径?
前端·ai编程·deepseek
cindershade2 小时前
React Server Components 在真实项目中的边界:哪些组件该放在服务端
前端
OpenTiny社区2 小时前
GenUI SDK v1.3.0 开发者深度解读:当生成式 UI 开始"长出"工程化骨架
前端·ai编程
前端粉刷匠2 小时前
2025 年是 Agent 的,2026 年是 Harness 的——AI 编程 Harness 架构深度解析
前端·人工智能