fix: chat page render performance (#2784)

* fix: chat page render performance

* fix: ts
This commit is contained in:
Archer
2024-09-24 18:05:27 +08:00
committed by GitHub
parent afd2c394d8
commit 7aa75f8ee0
7 changed files with 244 additions and 202 deletions

View File

@@ -103,3 +103,4 @@ weight: 813
17. 修复 - 知识库选择权限问题。
18. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。
19. 修复 - createDataset 接口intro 为赋值。
20. 修复 - 对话框渲染性能问题。

View File

@@ -11,12 +11,12 @@ import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
import { getChatRecords } from '@/web/core/chat/api';
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { GetChatRecordsProps } from '@/global/core/chat/api';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import { PaginationResponse } from '../../../../../../../packages/web/common/fetch/type';
import { PaginationResponse } from '@fastgpt/web/common/fetch/type';
import type { getPaginationRecordsBody } from '@/pages/api/core/chat/getPaginationRecords';
import { GetChatTypeEnum } from '@/global/core/chat/constants';
export const useChat = () => {
export const useChat = (params?: { chatId?: string; appId: string; type?: GetChatTypeEnum }) => {
const ChatBoxRef = useRef<ChatComponentRef>(null);
const variablesForm = useForm<ChatBoxInputFormType>();
// plugin
@@ -49,38 +49,41 @@ export const useChat = () => {
ChatBoxRef.current?.restartChat?.();
}, [variablesForm]);
const useChatScrollData = useCallback((params: GetChatRecordsProps) => {
return useScrollPagination(
async (data: getPaginationRecordsBody): Promise<PaginationResponse<ChatSiteItemType>> => {
const res = await getChatRecords(data);
const {
data: chatRecords,
ScrollData,
setData: setChatRecords,
total: totalRecordsCount
} = useScrollPagination(
async (data: getPaginationRecordsBody): Promise<PaginationResponse<ChatSiteItemType>> => {
const res = await getChatRecords(data);
// First load scroll to bottom
if (data.offset === 0) {
function scrollToBottom() {
requestAnimationFrame(
ChatBoxRef?.current ? () => ChatBoxRef?.current?.scrollToBottom?.() : scrollToBottom
);
}
scrollToBottom();
// First load scroll to bottom
if (data.offset === 0) {
function scrollToBottom() {
requestAnimationFrame(
ChatBoxRef?.current ? () => ChatBoxRef?.current?.scrollToBottom?.() : scrollToBottom
);
}
return {
...res,
list: res.list.map((item) => ({
...item,
dataId: item.dataId || getNanoid(),
status: ChatStatusEnum.finish
}))
};
},
{
pageSize: 10,
refreshDeps: [params],
params,
scrollLoadType: 'top'
scrollToBottom();
}
);
}, []);
return {
...res,
list: res.list.map((item) => ({
...item,
dataId: item.dataId || getNanoid(),
status: ChatStatusEnum.finish
}))
};
},
{
pageSize: 10,
refreshDeps: [params],
params,
scrollLoadType: 'top'
}
);
return {
ChatBoxRef,
@@ -89,7 +92,10 @@ export const useChat = () => {
setPluginRunTab,
clearChatRecords,
resetVariables,
useChatScrollData
chatRecords,
ScrollData,
setChatRecords,
totalRecordsCount
};
};

View File

@@ -47,14 +47,11 @@ const DetailLogsModal = ({
pluginRunTab,
setPluginRunTab,
resetVariables,
useChatScrollData
} = useChat();
const {
data: chatRecords,
chatRecords,
ScrollData,
setData: setChatRecords,
total: totalRecordsCount
} = useChatScrollData(params);
setChatRecords,
totalRecordsCount
} = useChat(params);
const { data: chat, isFetching } = useQuery(
['getChatDetail', chatId],

View File

@@ -164,4 +164,4 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
);
};
export default SliderApps;
export default React.memo(SliderApps);

View File

@@ -77,14 +77,11 @@ const Chat = ({
pluginRunTab,
setPluginRunTab,
resetVariables,
useChatScrollData
} = useChat();
const {
data: chatRecords,
chatRecords,
ScrollData,
setData: setChatRecords,
total: totalRecordsCount
} = useChatScrollData(params);
setChatRecords,
totalRecordsCount
} = useChat(params);
// get chat app info
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
@@ -166,6 +163,57 @@ const Chat = ({
);
const loading = isLoading;
const RenderHistorySlider = useMemo(() => {
const Children = (
<ChatHistorySlider
confirmClearText={t('common:core.chat.Confirm to clear history')}
appId={appId}
appName={chatData.app.name}
appAvatar={chatData.app.avatar}
onDelHistory={(e) => onDelHistory({ ...e, appId })}
onClearHistory={() => {
onClearHistories({ appId });
}}
onSetHistoryTop={(e) => {
onUpdateHistory({ ...e, appId });
}}
onSetCustomTitle={async (e) => {
onUpdateHistory({
appId,
chatId: e.chatId,
customTitle: e.title
});
}}
/>
);
return isPc || !appId ? (
<SideBar>{Children}</SideBar>
) : (
<Drawer
isOpen={isOpenSlider}
placement="left"
autoFocus={false}
size={'xs'}
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
</Drawer>
);
}, [
appId,
chatData.app.avatar,
chatData.app.name,
isOpenSlider,
isPc,
onClearHistories,
onCloseSlider,
onDelHistory,
onUpdateHistory,
t
]);
return (
<Flex h={'100%'}>
<NextHead title={chatData.app.name} icon={chatData.app.avatar}></NextHead>
@@ -179,43 +227,7 @@ const Chat = ({
<PageContainer isLoading={loading} flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
<Flex h={'100%'} flexDirection={['column', 'row']}>
{/* pc always show history. */}
{((children: React.ReactNode) => {
return isPc || !appId ? (
<SideBar>{children}</SideBar>
) : (
<Drawer
isOpen={isOpenSlider}
placement="left"
autoFocus={false}
size={'xs'}
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'75vw'}>{children}</DrawerContent>
</Drawer>
);
})(
<ChatHistorySlider
confirmClearText={t('common:core.chat.Confirm to clear history')}
appId={appId}
appName={chatData.app.name}
appAvatar={chatData.app.avatar}
onDelHistory={(e) => onDelHistory({ ...e, appId })}
onClearHistory={() => {
onClearHistories({ appId });
}}
onSetHistoryTop={(e) => {
onUpdateHistory({ ...e, appId });
}}
onSetCustomTitle={async (e) => {
onUpdateHistory({
appId,
chatId: e.chatId,
customTitle: e.title
});
}}
/>
)}
{RenderHistorySlider}
{/* chat container */}
<Flex
position={'relative'}

View File

@@ -4,11 +4,7 @@ import { Box, Flex, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/reac
import { streamFetch } from '@/web/common/api/fetch';
import SideBar from '@/components/SideBar';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet(
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSYZ1234567890_',
24
);
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
@@ -19,7 +15,6 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
import { useTranslation } from 'next-i18next';
import { delChatRecordById, getInitOutLinkChatInfo } from '@/web/core/chat/api';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
import { addLog } from '@fastgpt/service/common/system/log';
@@ -87,14 +82,6 @@ const OutLink = ({
onChangeChatId
} = useContextSelector(ChatContext, (v) => v);
const {
ChatBoxRef,
variablesForm,
pluginRunTab,
setPluginRunTab,
resetVariables,
useChatScrollData
} = useChat();
const params = useMemo(() => {
return {
chatId,
@@ -105,11 +92,16 @@ const OutLink = ({
};
}, [chatData.appId, chatId, outLinkUid, shareId]);
const {
data: chatRecords,
ChatBoxRef,
variablesForm,
pluginRunTab,
setPluginRunTab,
resetVariables,
chatRecords,
ScrollData,
setData: setChatRecords,
total: totalRecordsCount
} = useChatScrollData(params);
setChatRecords,
totalRecordsCount
} = useChat(params);
const startChat = useCallback(
async ({
@@ -233,6 +225,73 @@ const OutLink = ({
useMount(() => {
setIdEmbed(window !== top);
});
const RenderHistoryList = useMemo(() => {
const Children = (
<ChatHistorySlider
appName={chatData.app.name}
appAvatar={chatData.app.avatar}
confirmClearText={t('common:core.chat.Confirm to clear share chat history')}
onDelHistory={({ chatId }) =>
onDelHistory({ appId: chatData.appId, chatId, shareId, outLinkUid })
}
onClearHistory={() => {
onClearHistories({ shareId, outLinkUid });
}}
onSetHistoryTop={(e) => {
onUpdateHistory({
...e,
appId: chatData.appId,
shareId,
outLinkUid
});
}}
onSetCustomTitle={(e) => {
onUpdateHistory({
appId: chatData.appId,
chatId: e.chatId,
customTitle: e.title,
shareId,
outLinkUid
});
}}
/>
);
if (showHistory !== '1') return null;
return isPc ? (
<SideBar>{Children}</SideBar>
) : (
<Drawer
isOpen={isOpenSlider}
placement="left"
autoFocus={false}
size={'xs'}
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'75vw'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
{Children}
</DrawerContent>
</Drawer>
);
}, [
chatData.app.avatar,
chatData.app.name,
chatData.appId,
isOpenSlider,
isPc,
onClearHistories,
onCloseSlider,
onDelHistory,
onUpdateHistory,
outLinkUid,
shareId,
showHistory,
t
]);
const loading = isLoading;
return (
@@ -244,54 +303,7 @@ const OutLink = ({
: { p: [0, 5] })}
>
<Flex h={'100%'} flexDirection={['column', 'row']}>
{showHistory === '1' &&
((children: React.ReactNode) => {
return isPc ? (
<SideBar>{children}</SideBar>
) : (
<Drawer
isOpen={isOpenSlider}
placement="left"
autoFocus={false}
size={'xs'}
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'75vw'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
{children}
</DrawerContent>
</Drawer>
);
})(
<ChatHistorySlider
appName={chatData.app.name}
appAvatar={chatData.app.avatar}
confirmClearText={t('common:core.chat.Confirm to clear share chat history')}
onDelHistory={({ chatId }) =>
onDelHistory({ appId: chatData.appId, chatId, shareId, outLinkUid })
}
onClearHistory={() => {
onClearHistories({ shareId, outLinkUid });
}}
onSetHistoryTop={(e) => {
onUpdateHistory({
...e,
appId: chatData.appId,
shareId,
outLinkUid
});
}}
onSetCustomTitle={(e) => {
onUpdateHistory({
appId: chatData.appId,
chatId: e.chatId,
customTitle: e.title,
shareId,
outLinkUid
});
}}
/>
)}
{RenderHistoryList}
{/* chat container */}
<Flex

View File

@@ -67,14 +67,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
onChangeChatId
} = useContextSelector(ChatContext, (v) => v);
const {
ChatBoxRef,
variablesForm,
pluginRunTab,
setPluginRunTab,
resetVariables,
useChatScrollData
} = useChat();
const params = useMemo(() => {
return {
appId,
@@ -85,11 +77,16 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
};
}, [appId, chatId, teamId, teamToken]);
const {
data: chatRecords,
ChatBoxRef,
variablesForm,
pluginRunTab,
setPluginRunTab,
resetVariables,
chatRecords,
ScrollData,
setData: setChatRecords,
total: totalRecordsCount
} = useChatScrollData(params);
setChatRecords,
totalRecordsCount
} = useChat(params);
const startChat = useCallback(
async ({
@@ -181,6 +178,61 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
}
);
const RenderHistoryList = useMemo(() => {
const Children = (
<ChatHistorySlider
appId={appId}
appName={chatData.app.name}
appAvatar={chatData.app.avatar}
confirmClearText={t('common:core.chat.Confirm to clear history')}
onDelHistory={(e) => onDelHistory({ ...e, appId, teamId, teamToken })}
onClearHistory={() => {
onClearHistories({ appId, teamId, teamToken });
}}
onSetHistoryTop={(e) => {
onUpdateHistory({ ...e, teamId, teamToken, appId });
}}
onSetCustomTitle={async (e) => {
onUpdateHistory({
appId,
chatId: e.chatId,
customTitle: e.title,
teamId,
teamToken
});
}}
/>
);
return isPc || !appId ? (
<SideBar>{Children}</SideBar>
) : (
<Drawer
isOpen={isOpenSlider}
placement="left"
autoFocus={false}
size={'xs'}
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
</Drawer>
);
}, [
appId,
chatData.app.avatar,
chatData.app.name,
isOpenSlider,
isPc,
onClearHistories,
onCloseSlider,
onDelHistory,
onUpdateHistory,
t,
teamId,
teamToken
]);
const loading = isLoading;
return (
@@ -195,45 +247,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
<PageContainer isLoading={loading} flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
{((children: React.ReactNode) => {
return isPc || !appId ? (
<SideBar>{children}</SideBar>
) : (
<Drawer
isOpen={isOpenSlider}
placement="left"
autoFocus={false}
size={'xs'}
onClose={onCloseSlider}
>
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
<DrawerContent maxWidth={'75vw'}>{children}</DrawerContent>
</Drawer>
);
})(
<ChatHistorySlider
appId={appId}
appName={chatData.app.name}
appAvatar={chatData.app.avatar}
confirmClearText={t('common:core.chat.Confirm to clear history')}
onDelHistory={(e) => onDelHistory({ ...e, appId, teamId, teamToken })}
onClearHistory={() => {
onClearHistories({ appId, teamId, teamToken });
}}
onSetHistoryTop={(e) => {
onUpdateHistory({ ...e, teamId, teamToken, appId });
}}
onSetCustomTitle={async (e) => {
onUpdateHistory({
appId,
chatId: e.chatId,
customTitle: e.title,
teamId,
teamToken
});
}}
/>
)}
{RenderHistoryList}
{/* chat container */}
<Flex
position={'relative'}