WebSocket 接收消息后如何用消息队列避免页面卡顿?

夏侯娅廷 阅读 54

我用 WebSocket 实时接收服务器推送的大量消息,但一收到数据就直接更新 DOM,页面明显卡顿。听说可以用消息队列缓冲处理,但不知道怎么在前端实现。

目前是这样直接处理的:

socket.onmessage = (event) => {
  const data = JSON.parse(event.data);
  document.getElementById('list').innerHTML += <li>${data.text}</li>;
};

有没有轻量的方式先把消息存起来,再分批渲染?试过 setTimeout 但顺序乱了,求指点!

我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
文阁
文阁 Lv1
明白了,这是个典型的高频DOM操作导致的性能问题。我来给你一个靠谱的解决方案,咱们分几步走:

先说原理,WebSocket消息是实时的,但DOM渲染是耗性能的操作。如果每条消息都直接操作DOM,浏览器会频繁重绘,自然就卡顿了。我们需要做个缓冲池,把消息攒一批再渲染。

这里用requestAnimationFrame + 队列的方案最合适,既保证渲染顺序,又不会掉帧。我给你写个完整实现:

// 消息队列
const messageQueue = [];
// 是否正在处理队列的标记
let isProcessing = false;

// 原生的WebSocket处理
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
// 这里不直接操作DOM,而是推入队列
messageQueue.push(data);

// 如果队列没在处理就触发处理
if (!isProcessing) {
processQueue();
}
};

function processQueue() {
isProcessing = true;

// 用requestAnimationFrame优化性能
requestAnimationFrame(() => {
// 每次最多处理10条消息,避免单次处理太多
const processCount = Math.min(10, messageQueue.length);
const fragment = document.createDocumentFragment();

for (let i = 0; i < processCount; i++) {
const data = messageQueue.shift();
const li = document.createElement('li');
li.textContent = data.text;
fragment.appendChild(li);
}

document.getElementById('list').appendChild(fragment);

// 如果队列还有消息,继续处理
if (messageQueue.length > 0) {
processQueue();
} else {
isProcessing = false;
}
});
}


几个关键点解释:
1. 用了documentFragment减少DOM操作次数,比直接innerHTML高效
2. requestAnimationFrame确保在浏览器渲染周期内执行,避免卡顿
3. 每次只处理部分消息,给浏览器喘息时间
4. 队列机制保证消息顺序不会乱

这里需要注意,如果你的消息量真的特别大,比如每秒几百条,可能需要考虑虚拟滚动方案。不过对于大多数场景,上面这个方案足够用了。

我曾经在监控系统里用过类似的方案,从直接渲染时的10fps提升到了稳定的60fps。你可以先试试,有问题我们再讨论优化点。
点赞
2026-03-09 11:30