You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

类ChatGPT聊天应用流式响应优化:实时渲染与性能平衡咨询

类ChatGPT聊天应用实时渲染优化方案求助

我正在开发一款类ChatGPT的聊天应用,目前需要优化实时消息渲染的技术方案。之前尝试过两种渲染策略,但都存在问题:

  • 策略1:当消息缓冲区字符数超过50时更新Redux状态触发UI重渲染,但会出现响应卡顿。
  • 策略2:仅在收到完整响应的message.id时更新状态,减少了重渲染次数,但用户需要等待完整内容加载完成,期间只能看到占位符。

我希望实现类似ChatGPT的实时逐段/逐字显示效果,同时保证响应速度,避免频繁重渲染带来的性能损耗。以下是相关代码:

SocketService 代码

import {
  addMessage,
  modifyLatestMessage,
  removeLatestMessage,
  setError,
  setStatus,
  store,
} from "@/store";
import { io, Socket } from "socket.io-client";

const messageChunkSize = 50;

class SocketService {
  private socket: Socket | null = null;
  private messageBuffer = "";

  connectSocket = (sessionToken: string) => {
    if (!this.socket) {
      this.socket = io("https://sakkarai-backend.azurewebsites.net", {
        path: "/sockets/v1",
        auth: { token: sessionToken },
        transports: ["websocket"], // 优先使用WebSocket传输
        reconnectionAttempts: 5,
        timeout: 10000,
      });

      this.socket.on("connect", () => {
        console.log("Socket connected");
        store.dispatch(setStatus("CONNECTED"));
      });

      this.socket.on("disconnect", () => {
        console.log("Socket disconnected");
        store.dispatch(setStatus("DISCONNECTED"));
      });

      this.socket.on("receiveMessage", (message) => {
        console.log("Message received at:", new Date().toISOString(), message);
        let updateMessage = false;
        let payload: any = {};
        if (message.message) {
          this.messageBuffer += message.message;
          // if (this.messageBuffer.length >= messageChunkSize) {
          //   store.dispatch(
          //     modifyLatestMessage({ message: this.messageBuffer })
          //   );
          //   this.messageBuffer = "";
          // }
        }
        if (message.id) {
          store.dispatch(
            modifyLatestMessage({ message: this.messageBuffer, id: message.id })
          );
          this.messageBuffer = "";
        }
        if (message.commit) {
          store.dispatch(modifyLatestMessage({ commit: message.commit }));
        }
      });

      this.socket.on("error", (error: any) => {
        console.error("Socket error:", error);
        store.dispatch(setStatus("IDLE"));

        if (error.code === "insufficient-quota") {
          store.dispatch(removeLatestMessage("assistant"));
          store.dispatch(removeLatestMessage("user"));
          store.dispatch(setError(error.message));
        }
      });
    }
  };

  emitMessage = (message: string) => {
    if (this.socket && this.socket.connected) {
      store.dispatch(
        addMessage({
          content: message,
          createdAt: new Date(),
          role: "user",
          canCommit: false,
          user: null,
        })
      );

      store.dispatch(
        addMessage({
          content: "",
          createdAt: new Date(),
          role: "assistant",
          canCommit: false,
          user: null,
        })
      );

      this.socket.emit("sendMessage", { message });

      store.dispatch(setStatus("STREAMING"));
    } else {
      console.error("Socket is not connected!");
    }
  };

  disconnect() {
    if (this.socket) {
      this.socket.disconnect();
      this.socket = null;
      store.dispatch(setStatus("DISCONNECTED"));
    }
  }
}

export const socketService = new SocketService();

chatSlice 代码

export const chatSlice = createSlice({
  name: "chat",
  initialState,
  reducers: {
    setMessages: (state, action) => {
      state.messages = action.payload;
    },
    addMessage: (state, action: { payload: Message }) => {
      console.log("Adding Message", action.payload)
      state.messages.unshift(action.payload);
    },
    setStatus: (state, action) => {
      state.status = action.payload;
    },
    modifyLatestMessage: (
      state,
      action: {
        payload: { id?: string; message?: string; commit?: boolean };
      }
    ) => {
      if (state.messages.length > 0) {
        if (action.payload.message) {
          state.status = "STREAMING";
          state.messages[0].content += action.payload.message;
        }
        if (action.payload.commit) {
          state.messages[0].canCommit = action.payload.commit;
        }
        if (action.payload.id) {
          state.status = "IDLE";
          if (state.messageBuffer.length > 0) {
            state.messages[0].content += state.messageBuffer;
            state.messageBuffer = "";
          }
          state.messages[0].id = action.payload.id;
        }
      }
    },
    removeLatestMessage: (state, action: { payload: string }) => {
      const role = action.payload;
      const lastUserMessageIndex = state.messages.findIndex(
        (msg) => msg.role === role
      );
      if (lastUserMessageIndex !== -1) {
        state.messages.splice(lastUserMessageIndex, 1);
      }
    },
    resetChat: (state) => {
      state.messages = [];
      state.metadata = undefined;
    },
    setError: (state, action) => {
      state.error = action.payload;
    },
    clearError: (state) => {
      state.error = null;
    },
  },
  extraReducers: (builder) => {
    builder.addCase(fetchMessages.pending, (state) => {
      state.status = "LOADING_MESSAGES";
    });
    builder.addCase(fetchMessages.fulfilled, (state, action) => {
      state.messages.push(...action.payload.data);
      state.metadata = action.payload.metadata;
      state.status = "IDLE";
    });
    builder.addCase(fetchMessages.rejected, (state) => {
      state.status = "IDLE";
    });
    // builder.addCase(sendMessage.pending, (state) => {
    //   state.status = "STREAMING_MESSAGE";
    // });
    // builder.addCase(sendMessage.fulfilled, (state) => {
    //   if (commitmentParser.test(state.messages[0].content)) {
    //     state.messages[0].canCommit = true;
    //   }
    //   state.status = "IDLE";
    // });
    // builder.addCase(sendMessage.rejected, (state, action) => {
    //   state.status = "IDLE";
    //   state.messages.shift();
    //   if (action.payload === "insufficient-quota") {
    //     state.error = "You have exceeded your message quota.";
    //   }
    // });
    builder.addCase(clearConversation.pending, (state) => {
      state.status = "CLEARING_CHAT";
    });
    builder.addCase(clearConversation.fulfilled, (state) => {
      state.status = "IDLE";
    });
    builder.addCase(clearConversation.rejected, (state) => {
      state.status = "IDLE";
    });
  },
});

内容的提问来源于stack exchange,提问作者Hamza10

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 11:37:13