HarmonyOS 鸿蒙Next 6如何渲染扣子智能体的流式富文本回答(含 markdown / 代码块 / 图片)?
HarmonyOS 鸿蒙Next 6如何渲染扣子智能体的流式富文本回答(含 markdown / 代码块 / 图片)? 问题描述:扣子智能体返回流式 markdown 回答(含代码块、图片、列表),需在鸿蒙侧实时拼接流式片段,解析 markdown 并渲染为原生组件,避免渲染卡顿,如何实现? 关键字:鸿蒙 6、扣子智能体、流式富文本、markdown 解析、实时渲染
鸿蒙 6 渲染扣子智能体流式富文本的核心是「流式片段补全 + 轻量 Markdown 解析 + 原生组件分段渲染」,以下是极简实战方案,兼顾流畅性和易实现性:
一、核心步骤(极简版)
1. 前置准备
- 安装鸿蒙适配的轻量 Markdown 解析库:
ohpm install @ohos/marked --save; - 配置网络权限(
INTERNET)用于加载图片。
2. 核心代码(一站式实现)
import marked from '@ohos/marked';
import webSocket from '@ohos.net.webSocket';
import { LazyImage } from '@ohos/components';
@Entry
@Component
struct KouziRichText {
@State renderList: Array<{ type: string; content: any }> = []; // 渲染列表
private unclosedCode: string = ''; // 缓存未闭合的代码块
private ws: webSocket.WebSocket | null = null;
// 1. 解析Markdown片段为原生组件配置
parseMarkdown(chunk: string) {
// 补全未闭合的代码块
let fullChunk = this.unclosedCode + chunk;
const codeEndIndex = fullChunk.lastIndexOf('```');
if (fullChunk.includes('```') && codeEndIndex === fullChunk.length - 3) {
this.unclosedCode = ''; // 代码块闭合,清空缓存
} else if (fullChunk.includes('```')) {
this.unclosedCode = fullChunk.slice(codeEndIndex); // 缓存未闭合部分
fullChunk = fullChunk.slice(0, codeEndIndex);
}
// 自定义解析规则:映射Markdown到原生组件类型
const renderer = new marked.Renderer();
renderer.code = (code, lang) => ({ type: 'code', content: { code, lang } });
renderer.image = (url) => ({ type: 'image', content: url });
renderer.listitem = (text) => ({ type: 'list', content: text });
renderer.paragraph = (text) => ({ type: 'text', content: text });
// 解析并合并到渲染列表(微任务执行,不卡UI)
queueMicrotask(() => {
const parsed = marked.parse(fullChunk, { renderer });
this.renderList = [...this.renderList, ...(Array.isArray(parsed) ? parsed : [parsed])];
});
}
// 2. 连接扣子智能体流式接口
connectAgent() {
this.ws = webSocket.createWebSocket();
this.ws.connect('wss://www.kuaishou.com/openapi/v1/agent/streamChat');
this.ws.on('open', () => {
this.ws?.send(JSON.stringify({
agent_id: '你的智能体ID',
token: '你的智能体token',
query: '讲解鸿蒙图片上传,含代码和列表'
}));
});
// 接收流式片段并解析
this.ws.on('message', (chunk) => {
const text = new TextDecoder().decode(chunk);
this.parseMarkdown(text);
});
}
build() {
Column() {
// 3. 渲染原生组件
Scroll() {
Column() {
ForEach(this.renderList, (item) => {
MatchCase(item.type) {
case 'text': Text(item.content).fontSize(14).lineHeight(24);
case 'code': Text(`【${item.content.lang}代码】点击查看`).backgroundColor('#f5f5f5').padding(8).onClick(() => {
// 代码块弹窗展示
AlertDialog.show({ message: item.content.code });
});
case 'image': LazyImage({ src: item.content }).width('100%').height(200).objectFit(ImageFit.Contain);
case 'list': Row().children([Text('• '), Text(item.content)]);
}
});
}.padding(10);
}.flexGrow(1);
Button('发送提问').onClick(() => this.connectAgent()).margin(10);
}.width('100%').height('100%');
}
}
二、关键说明(避坑核心)
- 流式片段补全:仅缓存未闭合的代码块(```),避免解析出畸形内容;
- 轻量解析:用
marked将 Markdown 转为「文本 / 代码 / 图片 / 列表」类型,直接映射鸿蒙原生组件; - 性能优化:
- 解析逻辑放
queueMicrotask,不阻塞流式接收; - 图片用
LazyImage懒加载,代码块弹窗展示(避免大段代码卡 UI);
- 解析逻辑放
- 极简适配:无需复杂工具类,所有逻辑一站式实现,新手也能快速上手。
三、核心避坑点
Authorization/agent_id需替换为自己的扣子智能体配置;- 流式片段解码用
TextDecoder('utf-8'),避免中文乱码; - 代码块 / 列表等特殊元素需自定义解析规则,不能直接渲染 HTML。
更多关于HarmonyOS 鸿蒙Next 6如何渲染扣子智能体的流式富文本回答(含 markdown / 代码块 / 图片)?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next 6中,渲染扣子智能体的流式富文本内容,主要使用ArkUI的Web组件或自定义组件实现。对于Markdown和代码块,可通过集成第三方解析库(如markdown-it)将文本转换为HTML,再由Web组件渲染。图片渲染则使用Image组件,根据流式数据中的URL动态加载。整个过程需结合数据监听和组件动态更新机制,以实现流式内容的逐段渲染。
在HarmonyOS Next中渲染扣子智能体的流式富文本,关键在于结合ArkUI的声明式UI与动态更新能力。以下是核心实现方案:
-
流式数据拼接与状态管理
- 使用
@State或@Link装饰器管理文本内容字符串,流式数据到达时直接追加。 - 建议采用
StringBuilder或数组缓存片段,避免频繁触发UI更新。
- 使用
-
Markdown解析与组件映射
- 集成轻量级Markdown解析库(如
commonmark-java的ArkTS移植版),将流式文本转换为结构化数据(AST)。 - 建立AST节点到ArkUI组件的映射规则:
- 标题/段落 →
Text组件配合字体样式 - 代码块 →
Text+TextDecorationType.Underline+ 等宽字体,或自定义[@Component](/user/Component)实现语法高亮 - 图片 →
Image组件,通过PixelMap或网络链接加载 - 列表 →
Row+Text组合
- 标题/段落 →
- 集成轻量级Markdown解析库(如
-
实时渲染优化
- 使用
LazyForEach按片段增量渲染,避免长文本全量刷新。 - 对图片实现懒加载,监听
VisibleAreaChange事件控制加载时机。 - 复杂代码块采用异步解析,防止阻塞主线程。
- 使用
-
流式处理架构示例
[@Component](/user/Component) struct StreamRenderer { @State segments: string[] = [] build() { List() { ForEach(this.segments, (segment) => { MarkdownBlock({ content: segment }) }) } } // 流式数据入口 appendStreamData(chunk: string) { this.segments.push(chunk) } } -
性能关键点
- 解析与渲染分离:在后台线程完成Markdown到AST的转换,仅将AST结果交主线程渲染。
- 组件复用:对相同类型的富文本节点(如连续段落)启用组件复用机制。
- 避免深层嵌套:控制Markdown转换后的组件层级,减少布局计算开销。
此方案通过动态组件生成与增量更新平衡了实时性要求与渲染性能,可直接应用于智能对话、实时日志等流式富文本场景。

