历史消息懒加载

性能优化
This commit is contained in:
首都爱护动物协会 2024-12-07 12:27:16 +08:00
parent e35d928bcd
commit 14c9cb6001
3 changed files with 126 additions and 25 deletions

View File

@ -38,6 +38,7 @@
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"@types/react-infinite-scroll-component": "^5.0.0",
"adm-zip": "^0.5.16",
"docx": "^9.0.2",
"electron-log": "^5.1.5",
@ -48,6 +49,7 @@
"html2canvas": "^1.4.1",
"markdown-it": "^14.1.0",
"officeparser": "^4.1.1",
"react-infinite-scroll-component": "^6.1.0",
"webdav": "4.11.4"
},
"devDependencies": {

View File

@ -17,8 +17,10 @@ import { estimateHistoryTokens } from '@renderer/services/TokenService'
import { Assistant, Message, Model, Topic } from '@renderer/types'
import { captureScrollableDiv, runAsyncFunction, uuid } from '@renderer/utils'
import { t } from 'i18next'
import { flatten, last, reverse, take } from 'lodash'
import { flatten, last, take } from 'lodash'
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component'
import BeatLoader from 'react-spinners/BeatLoader'
import styled from 'styled-components'
import Suggestions from '../components/Suggestions'
@ -31,13 +33,53 @@ interface Props {
setActiveTopic: (topic: Topic) => void
}
interface LoaderProps {
$loading: boolean
}
const LoaderContainer = styled.div<LoaderProps>`
display: flex;
justify-content: center;
padding: 10px;
width: 100%;
background: var(--color-background);
opacity: ${(props) => (props.$loading ? 1 : 0)};
transition: opacity 0.3s ease;
pointer-events: none;
`
const ScrollContainer = styled.div`
display: flex;
flex-direction: column-reverse;
`
interface ContainerProps {
right?: boolean
}
const Container = styled(Scrollbar)<ContainerProps>`
display: flex;
flex-direction: column-reverse;
padding: 10px 0;
padding-bottom: 20px;
overflow-x: hidden;
background-color: var(--color-background);
`
const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
const [messages, setMessages] = useState<Message[]>([])
const [displayMessages, setDisplayMessages] = useState<Message[]>([])
const [hasMore, setHasMore] = useState(true)
const [isLoadingMore, setIsLoadingMore] = useState(false)
const containerRef = useRef<HTMLDivElement>(null)
const messagesRef = useRef(messages)
const { updateTopic, addTopic } = useAssistant(assistant.id)
const { showTopics, topicPosition, showAssistants, enableTopicNaming } = useSettings()
const messagesRef = useRef(messages)
const INITIAL_MESSAGES_COUNT = 30
const LOAD_MORE_COUNT = 20
messagesRef.current = messages
const maxWidth = useMemo(() => {
@ -158,7 +200,7 @@ const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
setActiveTopic(newTopic)
autoRenameTopic()
// 由于复制了消,消息中附带的文件的总数变了,需要更新
// 由于复制了消<EFBFBD><EFBFBD><EFBFBD>,消息中附带的文件的总数变了,需要更新
const filesArr = branchMessages.map((m) => m.files)
const files = flatten(filesArr).filter(Boolean)
files.map(async (f) => {
@ -197,7 +239,31 @@ const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
})
}, [assistant, messages])
const memoizedMessages = useMemo(() => reverse([...messages]), [messages])
// 初始化显示最新的消息
useEffect(() => {
if (messages.length > 0) {
const reversedMessages = [...messages].reverse()
setDisplayMessages(reversedMessages.slice(0, INITIAL_MESSAGES_COUNT))
setHasMore(messages.length > INITIAL_MESSAGES_COUNT)
}
}, [messages])
// 加载更多历史消息
const loadMoreMessages = useCallback(() => {
if (!hasMore || isLoadingMore) return
setIsLoadingMore(true)
setTimeout(() => {
const currentLength = displayMessages.length
const reversedMessages = [...messages].reverse()
const moreMessages = reversedMessages.slice(currentLength, currentLength + LOAD_MORE_COUNT)
setDisplayMessages((prev) => [...prev, ...moreMessages])
setHasMore(currentLength + LOAD_MORE_COUNT < messages.length)
setIsLoadingMore(false)
}, 300)
}, [displayMessages, hasMore, isLoadingMore, messages])
return (
<Container
@ -207,30 +273,34 @@ const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
ref={containerRef}
right={topicPosition === 'left'}>
<Suggestions assistant={assistant} messages={messages} />
{memoizedMessages.map((message, index) => (
<MessageItem
key={message.id}
message={message}
topic={topic}
index={index}
hidePresetMessages={assistant.settings?.hideMessages}
onSetMessages={setMessages}
onDeleteMessage={onDeleteMessage}
onGetMessages={onGetMessages}
/>
))}
<InfiniteScroll
dataLength={displayMessages.length}
next={loadMoreMessages}
hasMore={hasMore}
loader={null}
inverse={true}
scrollableTarget="messages">
<ScrollContainer>
<LoaderContainer $loading={isLoadingMore}>
<BeatLoader size={8} color="var(--color-text-2)" />
</LoaderContainer>
{displayMessages.map((message, index) => (
<MessageItem
key={message.id}
message={message}
topic={topic}
index={index}
hidePresetMessages={assistant.settings?.hideMessages}
onSetMessages={setMessages}
onDeleteMessage={onDeleteMessage}
onGetMessages={onGetMessages}
/>
))}
</ScrollContainer>
</InfiniteScroll>
<Prompt assistant={assistant} key={assistant.prompt} />
</Container>
)
}
const Container = styled(Scrollbar)`
display: flex;
flex-direction: column-reverse;
padding: 10px 0;
padding-bottom: 20px;
overflow-x: hidden;
background-color: var(--color-background);
`
export default Messages

View File

@ -2093,6 +2093,15 @@ __metadata:
languageName: node
linkType: hard
"@types/react-infinite-scroll-component@npm:^5.0.0":
version: 5.0.0
resolution: "@types/react-infinite-scroll-component@npm:5.0.0"
dependencies:
react-infinite-scroll-component: "npm:*"
checksum: 10c0/257e7b2fc6ebf200ada409a5c21415c1a56157807636405f3329521cf36344a81afa87f992497747534453e48145b0d4f32ccee80814548b466a0d83dfa23eec
languageName: node
linkType: hard
"@types/react@npm:*, @types/react@npm:^18.2.48":
version: 18.3.5
resolution: "@types/react@npm:18.3.5"
@ -2343,6 +2352,7 @@ __metadata:
"@types/node": "npm:^18.19.9"
"@types/react": "npm:^18.2.48"
"@types/react-dom": "npm:^18.2.18"
"@types/react-infinite-scroll-component": "npm:^5.0.0"
"@types/tinycolor2": "npm:^1"
"@vitejs/plugin-react": "npm:^4.2.1"
adm-zip: "npm:^0.5.16"
@ -2384,6 +2394,7 @@ __metadata:
react-dom: "npm:^18.2.0"
react-hotkeys-hook: "npm:^4.6.1"
react-i18next: "npm:^14.1.2"
react-infinite-scroll-component: "npm:^6.1.0"
react-markdown: "npm:^9.0.1"
react-redux: "npm:^9.1.2"
react-router: "npm:6"
@ -9965,6 +9976,17 @@ __metadata:
languageName: node
linkType: hard
"react-infinite-scroll-component@npm:*, react-infinite-scroll-component@npm:^6.1.0":
version: 6.1.0
resolution: "react-infinite-scroll-component@npm:6.1.0"
dependencies:
throttle-debounce: "npm:^2.1.0"
peerDependencies:
react: ">=16.0.0"
checksum: 10c0/8de02f178ae861880dddbd0c882dc70b55e21737b87fe428140d81a2c5d13c5eeba8a4fc260b1e86e4c556fdea333d6babaed55a9e5987a42b181b2d92f69cd8
languageName: node
linkType: hard
"react-is@npm:^16.13.1, react-is@npm:^16.7.0":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
@ -11546,6 +11568,13 @@ __metadata:
languageName: node
linkType: hard
"throttle-debounce@npm:^2.1.0":
version: 2.3.0
resolution: "throttle-debounce@npm:2.3.0"
checksum: 10c0/41648e4cf46f935818af32ecac34f9876c618f24e300551cbe3a0ca2c5828cb8d2f9b73e6e1e2f8c64237f70fbc8c541f9b5c9114da70b33b1ed10ba4cc6b15f
languageName: node
linkType: hard
"throttle-debounce@npm:^5.0.0, throttle-debounce@npm:^5.0.2":
version: 5.0.2
resolution: "throttle-debounce@npm:5.0.2"