共计 2125 个字符,预计需要花费 6 分钟才能阅读完成。
背景与痛点
在传统聊天界面中,消息通常是同步且线性的。但对接 AI 对话时,开发者会遇到几个特殊挑战:

- 消息流异步性:AI 响应时间不定,可能需要数秒才能返回完整结果,期间需要展示部分响应内容(即流式传输)
- 上下文管理:对话需要维护历史消息上下文,且可能涉及不同会话的隔离
- 性能压力:高频的消息更新会导致界面卡顿,尤其是在移动设备上
技术选型
Vue 3 组合式 API vs 选项式 API
组合式 API 在这种动态性强的场景有明显优势:
- 逻辑关注点更容易聚合(消息处理、状态管理、副作用可以放在一起)
- 自定义 hook 更利于复用(如
useMessageQueue) - TypeScript 支持更友好
Pinia vs Vuex
选择 Pinia 作为状态管理的主要考虑:
- 更简单的 API(不需要 mutations)
- 更好的 TypeScript 集成
- 更轻量(压缩后约 1KB)
- 组合式风格的 store 定义
核心实现
WebSocket 实时通信
// websocket.service.js
export function useChatSocket() {const socket = ref(null)
const messageQueue = ref([])
const connect = (url) => {socket.value = new WebSocket(url)
socket.value.onmessage = (event) => {const data = JSON.parse(event.data)
// 处理分块消息
if (data.isPartial) {updatePartialMessage(data)
} else {messageQueue.value.push(data)
}
}
}
return {socket, messageQueue, connect}
}
Pinia 消息队列方案
// stores/messageStore.js
export const useMessageStore = defineStore('messages', {state: () => ({activeMessages: [],
history: {},
currentSessionId: null
}),
actions: {appendMessage(message) {if (!this.history[this.currentSessionId]) {this.history[this.currentSessionId] = []}
this.activeMessages.push(message)
this.history[this.currentSessionId].push(message)
}
}
})
虚拟滚动优化
使用 vue-virtual-scroller 实现:
<template>
<RecycleScroller
:items="messages"
:item-size="72"
key-field="id"
v-slot="{item}"
>
<MessageBubble :message="item" />
</RecycleScroller>
</template>
完整代码示例
消息模型定义
interface ChatMessage {
id: string
content: string
sender: 'user' | 'ai'
timestamp: number
status: 'pending' | 'sent' | 'failed'
isPartial?: boolean
}
打字机效果
const typewriterEffect = (text, targetRef) => {
let i = 0
const interval = setInterval(() => {if (i < text.length) {targetRef.value += text.charAt(i)
i++
} else {clearInterval(interval)
}
}, 30)
}
性能优化
防抖处理
import {debounce} from 'lodash-es'
const updateQueue = debounce(() => {// 批量更新逻辑}, 150)
Chrome 性能测试
优化前:
– 脚本执行时间:1200ms
– 布局抖动:4 次
优化后:
– 脚本执行时间:300ms
– 布局抖动:0 次
生产环境指南
敏感词过滤
const filterSensitiveWords = (text) => {const words = ['敏感词 1', '敏感词 2']
let result = text
words.forEach(word => {result = result.replace(new RegExp(word, 'gi'), '***')
})
return result
}
移动端适配
常见陷阱:
- 输入框被键盘遮挡:使用
window.scrollTo保持可见 - 虚拟滚动在 iOS 上的性能问题:减少
item-size值 - 点击延迟:添加
fastclick库
总结
通过组合 Vue 3 的组合式 API、Pinia 和 WebSocket,我们构建了一个高性能的 AI 对话界面。关键点在于:
- 将消息处理逻辑集中管理
- 采用流式更新而非全量渲染
- 合理使用虚拟列表和防抖
这套方案已在实际项目中验证,可以支撑 1000+ 条消息的流畅交互。未来可以考虑加入消息分页加载、更精细的动画效果等优化。
正文完
