Vue3怎么从零搭建一套既好看又实用的Chat UI?
其实想做一套Chat UI不难,难的是好用——既要适配Web端PC和移动端,又要支持表情、图片、语音、文件、快捷回复这些高频功能,还要有流畅的加载、滚动、输入框联动动画,不能卡得让人不想用,今天咱们就拆解成核心步骤聊,从基础选型到细节打磨,连动画优化的小技巧都给你。
第一步:先搞定基础选型,别一开始就选太复杂的框架
很多人做Chat UI会直接冲成熟的开源组件库,比如Element Plus的Chat或者PrimeVue的Message,但如果你是做定制化强的产品(比如专属品牌的客服入口、带AI绘图/编程提示框的助手),现成组件可能满足不了,不如用轻量的库搭核心。
推荐搭核心的轻量组合
- UI骨架用Flexbox + Grid CSS:别小看纯CSS布局,现在Flexbox的align-self、order属性,Grid的minmax(),做自适应聊天气泡、消息分组(时间、日期)特别顺手,不用额外写一堆JS逻辑。
- 交互状态管理用Pinia:Vue3官方亲儿子,比Vuex简洁太多,存消息列表、用户信息、滚动位置这些全局状态刚好,写TypeScript支持也强。
- 表情选emoji-picker-vue3:体积小、支持自定义主题色、能插入自定义表情图标的库,适配移动端的点击放大,比自己写emoji遍历+筛选快10倍。
- 文件上传用Axios + FormData:不用加额外的大库,简单配置支持拖拽、粘贴上传就行。
- 输入框防抖节流用VueUse的useDebounceFn/useThrottleFn:官方推荐的工具库,小而全,滚动加载、输入联想都能用。
为什么选这个组合?因为开源组件库的冗余代码太多,比如Element Plus的Chat带了一堆默认的会话列表、头像展示逻辑,你要改得面目全非反而费时间;轻量组合完全可控,要什么功能加什么,加载速度也快——Web端首屏加载超过3秒,用户流失率就会飙升,这个组合打包后基础包也就几百KB。
第二步:搭页面结构,分模块别堆在一个Vue文件里
做复杂组件最怕单文件超过500行,后期维护像拆炸弹,Chat UI可以拆成5个核心模块:会话顶部栏、聊天气泡列表、快捷功能区、输入框组、移动端适配浮层。
会话顶部栏
这个模块的核心是品牌露出+操作入口:左边可以放品牌Logo/AI头像、用户昵称、在线状态;右边是最小化、关闭(如果是独立浮窗的话)、清空聊天记录、设置(切换主题色、开启/关闭快捷回复)这些操作按钮。
在线状态的细节要做好:在线”用绿色实心圆,“忙碌”用橙色实心圆带个旋转的点,“离线”用灰色空心圆——这些细节能给用户安全感,知道对方什么时候会回复。
主题色切换可以用CSS变量实现:在Pinia里存个themeColor,然后在根组件的style标签里用--chat-primary: v-bind(themeColor),这样所有用到主题色的地方(比如聊天气泡、发送按钮、选中的表情)都能实时更新,不用刷新页面。
聊天气泡列表
这是Chat UI的核心,也是最容易出问题的地方,重点要解决消息展示、消息分组、滚动定位、无限滚动加载这4个问题。
消息展示怎么才好看?
聊天气泡分两种:用户发的在右边,AI/客服发的在左边,宽度自适应但别超过容器的75%(PC端)/85%(移动端)——宽度太宽会显得很乱,太窄又浪费空间。
气泡的样式要注意圆角:左边气泡的左上圆角小一点(比如4px),右上右下左下大一点(比如16px);右边气泡反过来——这样看起来像两个人在面对面说话,有代入感。
消息的文字排版别太密:行高用1.6,字号用14px(PC端)/15px(移动端),颜色别用纯黑,用#333或者#222,纯黑太刺眼。
图片、文件、语音的展示要统一:图片用圆角裁剪,点击可以放大预览;文件要有文件名、文件大小、下载按钮;语音要有播放/暂停按钮、进度条、时长显示——这些功能网上有很多轻量的子组件可以参考,但要记得和你的主题色统一。
消息分组怎么分才清晰?
可以按5分钟内的消息为一组,同一组不显示头像和时间戳,只显示一条最后一条消息的上/下面的小气泡分隔;间隔超过5分钟的话,显示一条居中的时间戳(今天 14:30”),两边加淡灰色的横线——这样看起来简洁,不会满屏都是头像和时间戳。
时间戳的逻辑可以封装成一个VueUse的自定义函数,比如useFormatTime,传入时间戳,返回“刚刚”“5分钟前”“14:30”“昨天 14:30”“2024年5月20日 14:30”这种格式,比写一堆if-else方便。
滚动定位怎么才丝滑?
很多人做滚动定位会直接用scrollTop = scrollHeight,但这样太生硬,有个小技巧:用Vue3的nextTick配合element.scrollIntoView({ behavior: 'smooth', block: 'end' }),而且要给最新的一条消息加个ref,比如lastMessageRef,每次更新消息列表的时候,就调用lastMessageRef.value.scrollIntoView。
还有一个细节要注意:如果用户正在看历史消息,这时候来了新消息,要不要自动滚动到底?答案是别自动——可以在右下角加个“有新消息,点击跳转”的小圆点按钮,点击之后再滚动到底——这个细节能提升用户体验,不会打断用户看历史消息。
无限滚动加载怎么才不卡?
无限滚动加载可以用VueUse的useIntersectionObserver,给聊天气泡列表的第一条消息加个ref,比如firstMessageRef,监听它是否进入视口,如果进入的话,就去加载更多历史消息——比监听scrollTop的性能好很多,因为IntersectionObserver是浏览器原生的API,不会频繁触发。
加载历史消息的时候,要注意滚动位置的保持:比如加载了20条历史消息之前,scrollTop是500px,加载之后scrollTop应该变成500px + 新增消息的高度——不然会跳到底部,用户又得往上滑,保持滚动位置的小技巧是:先计算新增消息的高度(可以在加载之前先渲染一个隐藏的占位符,加载完成后再替换成真实内容,同时计算占位符的高度),然后在加载完成后用nextTick设置element.scrollTop += 占位符的高度。
快捷功能区
快捷功能区在输入框的上面,默认隐藏,点击输入框左边的“+”号按钮才会展开——展开的时候要有个淡入+上滑的动画,收拢的时候要有个淡出+下滑的动画,用Vue3的<Transition>组件就能实现。
快捷功能区可以包含这些功能:图片、拍照(移动端)、视频、语音、文件、定位、快捷回复——快捷回复可以用Pinia存,支持用户自定义添加和删除,点击之后直接发送消息,不用打字,特别适合客服场景。
输入框组
输入框组的核心是自适应高度+粘贴上传+发送按钮联动。
自适应高度可以用Vue3的watch监听输入框的内容变化,每次变化的时候,把输入框的高度设为auto,然后再设为scrollHeight——这样输入框的高度会随着内容的增加而增加,最多到5行(PC端)/3行(移动端),超过之后就会出现滚动条。
粘贴上传的细节要做好:比如粘贴图片的时候,先显示一个缩略图,然后再上传到服务器,上传的时候显示进度条,上传失败的话显示重试按钮——别等上传完成再显示图片,这样用户会有等待感。
发送按钮联动:比如输入框里没有内容的时候,发送按钮是灰色的,禁用状态;有内容的时候,发送按钮变成主题色,启用状态;输入框里有正在上传的文件的时候,发送按钮也是禁用状态——这些细节能避免用户误操作。
移动端适配浮层
如果你的Chat UI是独立浮窗的话,移动端适配要注意:浮窗的默认位置是右下角,大小是80px*80px的圆形按钮,点击之后展开成全屏的聊天界面——全屏聊天界面的顶部栏要高一点(比如60px),方便用户点击关闭按钮;输入框组要固定在底部,键盘弹出的时候要自动上推,不会被键盘挡住。
键盘弹出的自动上推可以用VueUse的useWindowScrollY监听页面的滚动,或者用CSS的position: sticky+padding-bottom: env(safe-area-inset-bottom)——这个env(safe-area-inset-bottom)是专门适配iPhone X及以上机型的刘海屏/圆角屏的,能避免底部栏被挡住。
第三步:细节打磨,让Chat UI更有温度
细节决定成败,一套好看又实用的Chat UI,离不开这些小细节:
加载动画
加载动画别用太复杂的,比如旋转的圆圈就行——用户发消息之后,AI/客服还没回复的时候,可以在聊天气泡列表的底部显示一个加载动画,和AI/客服的头像在一起;上传文件的时候,也可以在文件缩略图上显示一个进度条+旋转的小圆圈。
打字机效果
如果是AI助手的话,可以加个打字机效果——就是AI回复的消息一个字一个字地打出来,看起来更真实,有互动感,打字机效果可以用Vue3的setInterval或者requestAnimationFrame实现,注意打字的速度别太快也别太慢,大概100-150ms打一个字就行。
打字机效果还有个细节要注意:如果AI回复的消息里有代码块的话,代码块别用打字机效果,直接完整显示——不然用户等代码块打完会急死。
消息撤回
消息撤回功能很重要,特别是用户发错消息的时候,撤回的时间限制可以设为2分钟,和微信一样——撤回之后,显示一条灰色的小气泡,你撤回了一条消息”或者“AI撤回了一条消息”。
夜间模式
夜间模式可以和主题色一起用CSS变量实现——Pinia里存个isDark,然后在根组件的style标签里加条件判断,如果isDark为true的话,就把背景色设为#121212,文字颜色设为#E0E0E0,聊天气泡的颜色设为#2D2D2D(左边)和#1E88E5(右边)——夜间模式能保护用户的眼睛,特别是晚上聊天的时候。
快捷回复的高亮
如果快捷回复里有关键词的话,可以高亮显示——比如快捷回复是“请问您需要什么帮助?”,关键词是“帮助”,可以把“帮助”设为主题色,加粗——这样用户能更快找到自己需要的快捷回复。
第四步:性能优化,让Chat UI更流畅
性能优化是Chat UI的重中之重,特别是消息列表有很多条的时候,别让它卡得像幻灯片。
虚拟列表
虚拟列表是解决消息列表卡顿的最好办法——虚拟列表只会渲染视口内的几条消息,比如20条,滚动的时候会动态替换视口内的消息,这样不管消息列表有多少条,渲染压力都很小。
虚拟列表可以用VueUse的useVirtualList,不用自己写一堆复杂的逻辑——只需要传入消息列表、容器高度、每条消息的预估高度就行,剩下的VueUse会帮你搞定。
图片懒加载
图片懒加载可以用VueUse的useIntersectionObserver,只渲染视口内的图片,视口外的图片显示一个占位符——这样能减少首屏加载的时间,提升用户体验。
图片懒加载还有个细节要注意:图片加载完成之后,要更新虚拟列表中这条消息的高度——不然虚拟列表的滚动会有问题。
组件懒加载
如果你的Chat UI是作为项目的一个子页面的话,可以用Vue3的<Suspense>和defineAsyncComponent实现组件懒加载——只在用户打开Chat UI的时候才加载相关的组件,这样能减少首屏加载的时间。
写在最后
从零搭建一套既好看又实用的Vue3 Chat UI,其实没有想象中那么难——只要选对基础选型,分模块搭建页面结构,打磨好细节,优化好性能,就能做出一套让用户满意的Chat UI。
如果你时间比较紧张,或者不想自己写的话,也可以参考一些成熟的开源项目,比如OpenChat UI、ChatUIKit Vue——这些开源项目的代码质量都很高,而且可以自由修改,适合快速上线产品。
不过不管是自己写还是参考开源项目,都要记住:用户体验是第一位的——别为了追求好看而加一堆没用的功能,别为了追求功能而牺牲加载速度和流畅度。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


