🚀 前言:当国产之光遇见国产之光
市面上全是 Web 版的套壳 AI,体验极其卡顿。
作为鸿蒙开发者,我们要做就做纯原生 (Native)的。
本教程将带你从零开始,使用ArkTS对接 DeepSeek 官方 API,并攻克最大的技术难点——在鸿蒙原生列表里完美渲染 Markdown 代码块。
🏗️ 一、 架构设计:数据流转图
我们要实现的 App 包含三个核心层级:
- UI 层:基于
List的聊天气泡界面。 - 渲染层:封装
Web组件,通过loadData动态渲染 Markdown。 - 数据层:使用
http模块发起 SSE (流式) 或普通 POST 请求。
系统架构 (Mermaid):
🛠️ 二、 核心实战:一步步手撸代码
1. 配置网络权限
首先,别忘了在module.json5中申请网络权限,否则 App 连网都连不上。
"requestPermissions":[{"name":"ohos.permission.INTERNET"}]2. 封装 DeepSeek API 服务
DeepSeek 兼容 OpenAI 的 API 格式。我们封装一个简单的请求方法。
// DeepSeekService.etsimporthttpfrom'@ohos.net.http';exportclassDeepSeekService{privatestaticAPI_URL='https://api.deepseek.com/chat/completions';privatestaticAPI_KEY='你的_DEEPSEEK_API_KEY';// 记得替换!staticasyncchat(message:string):Promise<string>{lethttpRequest=http.createHttp();try{letresponse=awaithttpRequest.request(this.API_URL,{method:http.RequestMethod.POST,header:{'Content-Type':'application/json','Authorization':`Bearer${this.API_KEY}`},extraData:JSON.stringify({model:"deepseek-chat",messages:[{role:"system",content:"你是 DeepSeek,一个乐于助人的 AI 助手。"},{role:"user",content:message}],stream:false// 教程演示简单起见,暂不使用流式})});if(response.responseCode===200){// 解析返回结果constresult=JSON.parse(response.resultasstring);returnresult.choices[0].message.content;}else{return"网络请求失败: "+response.responseCode;}}catch(err){return"发生错误: "+JSON.stringify(err);}finally{httpRequest.destroy();}}}3. 攻克难点:Markdown 渲染组件 (MarkdownView)
鸿蒙原生的RichText功能有限。为了支持代码高亮和表格,最稳妥的方案是使用 Web 组件加载本地 HTML 模板,并注入marked.js和highlight.js。
// MarkdownView.etsimportweb_webviewfrom'@ohos.web.webview';@Componentexportstruct MarkdownView{@Propcontent:string;// 接收 Markdown 文本controller:web_webview.WebviewController=newweb_webview.WebviewController();// 构建一个包含 Markdown 解析库的 HTML 模板// 在真实项目中,建议将 marked.min.js 放进 rawfile 资源中读取privatehtmlTemplate(mdContent:string):string{return`<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown-light.min.css"> <style> body { padding: 10px; background-color: transparent; } </style> </head> <body> <div id="content" class="markdown-body"></div> <script> document.getElementById('content').innerHTML = marked.parse(\`${mdContent.replace(/`/g,'\\`')}\`); </script> </body> </html>`;}build(){// 关键:使用 Web 组件渲染 HTMLWeb({src:'',controller:this.controller}).onControllerAttached(()=>{// 当 Web 组件加载完成后,注入内容this.controller.loadData(this.htmlTemplate(this.content),"text/html","UTF-8");}).width('100%')// 根据内容动态高度是个大坑,这里简化处理,固定高度或使用 JS 交互算高度.height(300).backgroundColor(Color.Transparent)}}4. 组装主界面 (Index.ets)
最后,把聊天列表、输入框和我们的MarkdownView组装起来。
import{DeepSeekService}from'./DeepSeekService';import{MarkdownView}from'./MarkdownView';// 定义消息模型classChatMessage{id:number;role:'user'|'ai';content:string;constructor(role:'user'|'ai',content:string){this.id=Date.now();this.role=role;this.content=content;}}@Entry@Componentstruct Index{@Statemessages:ChatMessage[]=[];@StateinputText:string="";@StateisLoading:boolean=false;scroller:Scroller=newScroller();asyncsendMessage(){if(this.inputText.trim()==="")return;// 1. 添加用户消息constuserMsg=this.inputText;this.messages.push(newChatMessage('user',userMsg));this.inputText="";this.isLoading=true;// 2. 调用 DeepSeek APIconstaiResponse=awaitDeepSeekService.chat(userMsg);// 3. 添加 AI 消息this.messages.push(newChatMessage('ai',aiResponse));this.isLoading=false;// 滚动到底部this.scroller.scrollEdge(Edge.Bottom);}build(){Column(){// 标题Text("DeepSeek Harmony").fontSize(20).fontWeight(FontWeight.Bold).height(50)// 聊天列表区List({scroller:this.scroller}){ForEach(this.messages,(msg:ChatMessage)=>{ListItem(){Row(){if(msg.role==='user'){// 用户消息 (右侧)Text(msg.content).backgroundColor('#95EC69').padding(10).borderRadius(8).margin({left:50})}else{// AI 消息 (左侧,支持 Markdown)Column(){MarkdownView({content:msg.content})}.backgroundColor('#FFFFFF').borderRadius(8).margin({right:20}).width('90%')}}.width('100%').justifyContent(msg.role==='user'?FlexAlign.End:FlexAlign.Start).padding(10)}})}.layoutWeight(1)// 占据剩余空间.backgroundColor('#F5F5F5')// 底部输入区Row(){TextInput({text:this.inputText,placeholder:"问点什么..."}).onChange((value)=>this.inputText=value).layoutWeight(1).backgroundColor(Color.White).margin({right:10})Button(this.isLoading?"思考中...":"发送").onClick(()=>this.sendMessage()).enabled(!this.isLoading)}.padding(10).backgroundColor('#EEEEEE')}.height('100%')}}💡 三、 避坑指南与性能优化
- Web 组件高度自适应:
上面的代码为了演示给MarkdownView写死了高度。在生产环境中,你需要使用Web组件的onConsole或 JSBridge,在 HTML 渲染完成后将document.body.scrollHeight传回给 ArkTS,然后动态设置组件高度,否则长文会被截断。 - 流式响应 (Stream):
DeepSeek 支持 Stream 模式(打字机效果)。在鸿蒙中,这需要使用http模块的on('headerReceive')和on('dataReceive')监听事件,逐步拼接字符串并刷新 UI。这能极大提升用户体验。 - API Key 安全:
永远不要把 API Key 硬编码在客户端代码里上传到 Git!建议通过自己的后端服务器中转请求。
🎯 总结
通过不到 200 行代码,我们就在 HarmonyOS Next 上跑通了一个“DeepSeek”客户端。
这不仅验证了鸿蒙生态的开发效率,也展示了ArkTS + Web 组件混合开发的强大能力。
Next Step:
尝试给你的 App 加上“流式输出”功能,让 AI 的回复像打字机一样一个个蹦出来,体验瞬间提升 10 倍!