perf: full-stack performance optimization

Frontend:
- Debounce queryStore tab persistence (remove flush:sync deep watcher)
- Eliminate unnecessary object spreading in connectionStore error handling
- Use in-place delete for completion cache invalidation
- Deduplicate schema cache prefix API calls
- Split echarts, reka-ui, marked, @codemirror/commands into separate chunks
- Lazy-load xlsxExport and markdownTable in DataGrid
- Lazy-load marked parser in UpdateDialog

Backend:
- Replace tokio::sync::Mutex with RwLock for AppState connections/configs
  (60+ concurrent reads no longer block each other)
- Parallelize PostgreSQL DDL metadata queries with tokio::try_join!
This commit is contained in:
t8y2
2026-05-10 23:23:22 +08:00
parent dbfe69572e
commit bafb424b23
13 changed files with 126 additions and 99 deletions
+2 -2
View File
@@ -74,8 +74,6 @@ import {
} from "@/lib/gridSelection";
import { buildTableSelectSql, quoteTableIdentifier } from "@/lib/tableSelectSql";
import { buildDataGridRollbackStatements, buildDataGridSaveStatements, formatGridSqlLiteral } from "@/lib/dataGridSql";
import { formatMarkdownTable } from "@/lib/markdownTable";
import { buildXlsxWorkbook } from "@/lib/xlsxExport";
import {
matchesRowStatusFilter,
rowStatusFilterAfterAddingRow,
@@ -2019,6 +2017,7 @@ async function exportMarkdown() {
try {
const cols = props.result.columns;
const visibleRows = displayItems.value.map((item) => item.data);
const { formatMarkdownTable } = await import("@/lib/markdownTable");
const md = formatMarkdownTable({ columns: cols, rows: visibleRows });
if (await saveFileContent(md, "export.md", "Markdown", "md")) {
toast(t("grid.exported"));
@@ -2030,6 +2029,7 @@ async function exportMarkdown() {
async function exportXlsx() {
try {
const { buildXlsxWorkbook } = await import("@/lib/xlsxExport");
const workbook = buildXlsxWorkbook({
sheetName: props.tableMeta?.tableName || "Export",
columns: props.result.columns,
+15 -7
View File
@@ -1,7 +1,6 @@
<script setup lang="ts">
import { computed } from "vue";
import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { Marked } from "marked";
import { Loader2 } from "lucide-vue-next";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
@@ -27,12 +26,21 @@ const emit = defineEmits<{
const { t } = useI18n();
const isDesktop = isTauriRuntime();
const marked = new Marked({ breaks: true, gfm: true });
const renderedNotes = ref("");
const renderedNotes = computed(() => {
if (!props.updateInfo?.release_notes) return "";
return marked.parse(props.updateInfo.release_notes) as string;
});
watch(
() => props.updateInfo?.release_notes,
async (notes) => {
if (!notes) {
renderedNotes.value = "";
return;
}
const { Marked } = await import("marked");
const marked = new Marked({ breaks: true, gfm: true });
renderedNotes.value = marked.parse(notes) as string;
},
{ immediate: true },
);
</script>
<template>