Element-Plus-X:Vue 3 + Element Plus 的 AI 组件库,与 useXStream 流式 hook
项目信息
- GitHub 仓库:element-plus-x/Element-Plus-X
- Gitee 镜像:he-jiayue/element-plus-x
- 文档:element-plus-x.com
- 在线预览:v.element-plus-x.com
- npm 包名:
vue-element-plus-x - 问题反馈:Issues
- 模板项目预览:chat.element-plus-x.com,源码:HeJiaYue520/ruoyi-element-ai
生态相关:element-ui-x(Vue 2 版本)、hook-fetch(请求库)、ChatArea(轻量级聊天框)。
定位是企业级 AI 组件库,基于 Vue 3 + Element Plus,MIT 协议。特性上强调三点:聊天机器人与语音交互类组件、零配置接入 Element Plus 设计体系、按需加载与 Tree Shaking。
安装与引入
npm install vue-element-plus-x
pnpm add vue-element-plus-x
yarn add vue-element-plus-x
按需引入:
import { BubbleList, XSender } from 'vue-element-plus-x';
const list = [{ content: 'Hello, Element Plus X', role: 'user' }];
全局引入:
import ElementPlusX from 'vue-element-plus-x';
app.use(ElementPlusX);
CDN 引入(README 注明「该方法有待测试」):
版本策略是 SemVer,latest 为稳定版、beta 为测试版,装测试版:pnpm install vue-element-plus-x@beta。
本地开发:
pnpm install
pnpm run dev:docs # 文档
pnpm run dev:core # Storybook
pnpm run build:core
组件与 Hooks 清单
组件:
- Bubble 气泡消息组件(拓展)
- BubbleList 气泡消息列表(拓展)
- Conversations 会话管理组件(拓展)
- Welcome 欢迎组件
- Prompts 提示集组件
- FilesCard 文件卡片组件
- Attachments 上传附件组件
- XSender 智能输入框(含语音交互、提及功能)
- Thinking 思考中组件(拓展)
- ThoughtChain 思考链组件
文档侧边栏另有:EditorSender 编辑输入框、XMarkdown 渲染组件、Sender 输入框、MentionSender 提及输入框、Typewriter 打字器。
Hooks:
- useRecord:基于浏览器内置语音识别 API 的 Hooks
- useXStream:流模式接口 Hooks
- useSend & XRequest:流模式 hooks 的拆分(拓展)
useXStream
这个 hook 负责流式请求的全流程控制:发起请求、中断请求、返回 loading 状态、返回 SSE 协议下的实时数据流、返回 error 信息。它对标 ant-design-x 的 XStream,融合 Vue 开发范式,新增流式中断处理,并把原配置项收进 startStream 的入参。
文档注明:目前只测试了 SSE 和 SIP 协议请求,其他协议还有待测试。
SSE 基础用法
import { useXStream } from 'vue-element-plus-x';
const { startStream, cancel, data, error, isLoading } = useXStream();
async function startSSE() {
try {
const response = await fetch('https://node-test.element-plus-x.com/api/sse', {
headers: { 'Content-Type': 'text/event-stream' }
});
const readableStream = response.body;
await startStream({ readableStream });
} catch (err) {
console.error('Fetch error:', err);
}
}
注意这里的前提:请求本身由业务侧用 fetch 发出,hook 接收的是 response.body 这个 ReadableStream,不是 URL。也就是说,鉴权、header、参数拼装都在自己的 fetch 里完成。
数据拼接:
const content = computed(() => {
if (!data.value.length) return '';
let text = '';
for (let index = 0; index void` | 发起流式请求,可传入自定义 TransformStream |
| `cancel` | `() => void` | 中断当前流式请求 |
| `isLoading` | `Ref` | 是否正在请求(文档标题写作 `loading`,示例解构用 `isLoading`) |
| `data` | `Ref>` | 实时返回的流式数据,元素取 `data.value[index].data` |
| `error` | `Ref` | 流式请求的报错信息,可读 `error.message` |
### 默认解析规则
解析规则与 ant-design-x 一致,全部在 hook 内部完成,业务侧拿到的已经是拆好的 `data` 数组:
| 常量 | 值 | 作用 |
| --- | --- | --- |
| `DEFAULT_STREAM_SEPARATOR` | `'\n\n'` | 分割一条流数据 |
| `DEFAULT_PART_SEPARATOR` | `'\n'` | 换行当前数据 |
| `DEFAULT_KV_SEPARATOR` | `':'` | 键值对分隔 |
| `data:` 前缀 | — | SSE 字段前缀 |
| `[DONE]` | — | 结束标识,由后端决定是否返回 |
SSE 事件样例:
event: message
data: {"id":"${i}","content":"${contentChunks[i]}"}
### 非 SSE 协议:自定义 TransformStream
SIP 之类不符合 SSE 格式的协议,同样先 `fetch` 拿到 `response.body`,再自己构造 TransformStream 处理分片:
```js
const sipTransformStream = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk);
}
});
await startStream({ readableStream, transformStream: sipTransformStream });
transformStream 的职责是把原始分片整理成 hook 能按默认规则解析的形态,格式对齐的工作放在这里,而不是散在业务代码里。
已知边界
- 协议支持:文档明确只测过 SSE 与 SIP,其余协议待测。
- CDN 引入方式在 README 中标注「有待测试」,生产接入优先走 npm 包。
[DONE]是否出现由后端决定,前端解析循环里需要自己兜住这个分支。data里的每一项是 hook 拆好的单条 chunk,JSON.parse失败通常意味着后端返回的不是约定结构,或流已结束。