perf: 优化数据库连接与笔记预览加载性能

This commit is contained in:
ikmkj
2026-03-03 17:28:43 +08:00
parent f0f9b3f52f
commit 5a24569ebd
4 changed files with 63 additions and 26 deletions

View File

@@ -14,14 +14,16 @@ spring:
maximum-pool-size: 20
# 连接空闲超时时间(毫秒)
idle-timeout: 300000
# 连接最大存活时间(毫秒)
max-lifetime: 1200000
# 连接最大存活时间(毫秒)- 设置为小于MySQL的wait_timeout默认8小时
max-lifetime: 600000
# 连接超时时间(毫秒)
connection-timeout: 20000
# 测试连接是否可用的SQL
connection-test-query: SELECT 1
# 自动提交
auto-commit: true
# 连接测试间隔
keepalive-time: 300000
jpa:
hibernate:
ddl-auto: update

View File

@@ -14,14 +14,16 @@ spring:
maximum-pool-size: 50
# 连接空闲超时时间(毫秒)
idle-timeout: 600000
# 连接最大存活时间(毫秒)
max-lifetime: 1800000
# 连接最大存活时间(毫秒)- 设置为小于MySQL的wait_timeout默认8小时
max-lifetime: 600000
# 连接超时时间(毫秒)
connection-timeout: 30000
# 测试连接是否可用的SQL
connection-test-query: SELECT 1
# 自动提交
auto-commit: true
# 连接测试间隔
keepalive-time: 300000
jpa:
hibernate:
ddl-auto: update

View File

@@ -366,10 +366,17 @@ const previewFile = async (file) => {
selectedFile.value = null;
return;
}
// 先立即显示预览页(加载状态),让用户感知到响应
selectedFile.value = { ...file, content: '', isLoading: true };
showEditor.value = false;
// 异步加载内容
try {
const content = await Preview(file.id) || '';
selectedFile.value = { ...file, content };
showEditor.value = false;
// 内容加载完成后更新
if (selectedFile.value && selectedFile.value.id === file.id) {
selectedFile.value = { ...file, content, isLoading: false };
}
} catch (error) {
ElMessage.error('获取笔记内容失败: ' + error.message);
selectedFile.value = null;
@@ -552,29 +559,27 @@ const handleResize = () => {
}
};
// 使用防抖优化 Vditor 渲染
let renderTimeout = null;
let lastRenderedId = null;
// Vditor 渲染优化
let lastRenderedKey = null;
watch([selectedFile, showEditor], ([newFile, newShowEditor]) => {
if (newFile && !newShowEditor) {
// 如果同一个文件已经渲染过,跳过
if (lastRenderedId === newFile.id) return;
// 使用文件ID+内容长度作为渲染标识,内容变化时重新渲染
const renderKey = `${newFile.id}-${newFile.content?.length || 0}`;
if (lastRenderedKey === renderKey) return;
clearTimeout(renderTimeout);
renderTimeout = setTimeout(() => {
nextTick(() => {
const previewElement = document.querySelector('.markdown-preview');
if (previewElement) {
const contentToRender = (newFile.isPrivate === 1 && !userStore.isLoggedIn) ? privateNoteContent : newFile.content;
Vditor.preview(previewElement, contentToRender || '', {
mode: 'light',
hljs: { enable: true, style: 'github' }
});
lastRenderedId = newFile.id;
}
});
}, 50); // 50ms 防抖
// 使用 requestAnimationFrame 确保流畅渲染
requestAnimationFrame(() => {
const previewElement = document.querySelector('.markdown-preview');
if (previewElement) {
const contentToRender = (newFile.isPrivate === 1 && !userStore.isLoggedIn) ? privateNoteContent : newFile.content;
Vditor.preview(previewElement, contentToRender || '', {
mode: 'light',
hljs: { enable: true, style: 'github' }
});
lastRenderedKey = renderKey;
}
});
}
}, { deep: true });

View File

@@ -41,11 +41,16 @@
<div :key="file.id" class="markdown-preview">
<!-- Content is rendered by Vditor.preview in the parent -->
</div>
<!-- 加载状态遮罩 -->
<div v-if="file.isLoading" class="content-loading">
<el-icon class="loading-icon is-loading"><Loading /></el-icon>
<span class="loading-text">内容加载中...</span>
</div>
</div>
</template>
<script setup>
import { Lock, Edit, Delete, ArrowDown, Back } from '@element-plus/icons-vue';
import { Lock, Edit, Delete, ArrowDown, Back, Loading } from '@element-plus/icons-vue';
const props = defineProps({
file: {
@@ -76,6 +81,7 @@ const handleExport = (format) => {
display: flex;
flex-direction: column;
height: 100%;
position: relative;
}
.preview-header {
@@ -159,4 +165,26 @@ const handleExport = (format) => {
.markdown-preview::-webkit-scrollbar-thumb:hover {
background-color: #909399;
}
/* 加载状态 */
.content-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
color: var(--el-color-primary);
}
.loading-icon {
font-size: 32px;
}
.loading-text {
font-size: 14px;
color: var(--text-color-secondary);
}
</style>