Vue/JavaScript中如何实现聊天消息按日期有序分组排列
问题背景
当前使用Vue开展开发工作,遇到通用JavaScript问题:调用接口获取聊天UI所需的聊天消息,接口初始返回由聊天消息对象组成的数组,数据结构如下:
data: [ 0: {id: 1, created_at: "2022-05-20T15:12:40.000000Z", updated_at: "2022-05-20T17:18:03.000000Z",…} 1: {id: 2, created_at: "2022-05-20T15:12:41.000000Z", updated_at: "2022-05-20T17:18:04.000000Z",…} 2: {id: 3, created_at: "2022-05-20T15:12:41.000000Z", updated_at: "2022-05-20T17:18:04.000000Z",…} 3: {id: 4, created_at: "2022-05-20T15:12:41.000000Z", updated_at: "2022-05-20T17:18:04.000000Z",…} ]
需求是对消息做格式化处理,在聊天窗口中按日期对消息分组。最初编写的分组逻辑为Vue中的computed计算属性,代码如下:
const formattedChats = computed(() => { let dateFormattedMessages = messages.value.map(message => { return {...message, updated_at: new Date(message.updated_at).toDateString(), created_at: new Date(message.created_at).toDateString()} }) return dateFormattedMessages.reduce((total, currentValue) => { total[currentValue.updated_at] = total[currentValue.updated_at] || []; total[currentValue.updated_at].push(currentValue); return total; }, Object.create(null)); })
原代码逻辑为:先遍历每个聊天对象,将其updated_at和created_at字段转换为日期字符串,再基于updated_at字段对数组做分组,运行后得到的结果如下:
formattedChats = { Fri Jun 24 2022: [ {...}, {... ] Fri May 20 2022:[ {...}, {... ] Mon Jun 27 2022:Array[ {...}, {... ] Sat May 21 2022:Array[ {...}, {... ] Tue Jun 28 2022:Array[ {...}, {... ] }
存在的问题
- 分组后生成的对象日期键没有按时间先后顺序排列,直接渲染到UI会导致聊天消息日期顺序混乱,不符合聊天界面的预期展示效果。
- 本质原因是普通JavaScript对象的键排序规则不可靠,且原始逻辑没有做时间排序处理,遍历消息时的键插入顺序和日期先后顺序不一致。
解决方案
不要依赖普通对象的键顺序做UI渲染,改为输出有序数组结构供模板遍历,同时前置排序逻辑保证时间顺序正确,修正后的计算属性代码如下:
const formattedChats = computed(() => { // 格式化日期字段,同时保留时间戳用于排序,避免重复转日期 const processedMessages = messages.value.map(message => { const updateTime = new Date(message.updated_at) const createTime = new Date(message.created_at) return { ...message, _updateTimestamp: updateTime.getTime(), updated_at: updateTime.toDateString(), created_at: createTime.toDateString() } }) // 先对单条消息按更新时间从早到晚排序,需要最新消息在前就交换a、b顺序 processedMessages.sort((a, b) => a._updateTimestamp - b._updateTimestamp) // 按日期分组 const groupMap = Object.create(null) processedMessages.forEach(msg => { groupMap[msg.updated_at] = groupMap[msg.updated_at] || [] groupMap[msg.updated_at].push(msg) }) // 转为有序数组,兜底做日期排序,直接供模板渲染 return Object.entries(groupMap) .map(([dateLabel, msgList]) => ({ date: dateLabel, messages: msgList })) .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) })
模板层直接遍历返回的有序数组即可,无需额外处理排序:
<template> <div class="chat-wrapper"> <div v-for="dateGroup in formattedChats" :key="dateGroup.date" class="date-block"> <div class="date-tag">{{ dateGroup.date }}</div> <div v-for="msg in dateGroup.messages" :key="msg.id" class="message-item"> <!-- 消息内容渲染逻辑 --> </div> </div> </div> </template>
注意:不同JS引擎对普通对象的键排序规则存在差异,整数键、字符串键、Symbol键的排序逻辑不统一,涉及有序渲染的场景优先使用数组结构,可控性和兼容性更好。
内容的提问来源于stack exchange,提问作者NwaiwuIsidore
相关产品推荐
相关产品推荐

