From b5dc9c8a872225ed55e3b2a6d1ff1007e697d4e1 Mon Sep 17 00:00:00 2001 From: HouYunFei <1844025705@qq.com> Date: Sat, 18 Jul 2026 08:27:20 +0800 Subject: [PATCH] feat(canvas): optimize CanvasAssetsTab and CanvasPromptsTab with memoization to improve performance --- CHANGELOG.md | 1 + web/src/components/canvas/canvas-side-panel.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95047e5..93caec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased + [新增] 左侧画布面板新增「提示词库」Tab,按来源折叠分组。 ++ [修复] 处于「提示词库/资产」Tab 时拖动画布元素卡顿,面板内容不再随节点更新重渲染。 + [新增] 提示词来源增加自定义脚本抓取功能、支持新增来源。 ## v0.9.0 - 2026-07-17 diff --git a/web/src/components/canvas/canvas-side-panel.tsx b/web/src/components/canvas/canvas-side-panel.tsx index 264e4a5..c60d5ec 100644 --- a/web/src/components/canvas/canvas-side-panel.tsx +++ b/web/src/components/canvas/canvas-side-panel.tsx @@ -1,4 +1,4 @@ -import { useMemo, useRef, useState, type PointerEvent as ReactPointerEvent } from "react"; +import { memo, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent } from "react"; import { App, Empty, Input, Popconfirm, Select, Spin, Tag } from "antd"; import { useQuery } from "@tanstack/react-query"; import { BookOpen, Check, ChevronRight, Download, Eye, FileText, Image as ImageIcon, ListChecks, Music2, Plus, Search, Settings2, Square, Trash2, Type, Video } from "lucide-react"; @@ -286,7 +286,7 @@ function buildInsertPayload(asset: Asset): InsertAssetPayload { return { kind: "image", dataUrl: asset.data.dataUrl, storageKey: asset.data.storageKey, title: asset.title }; } -function CanvasAssetsTab({ onInsert, theme }: { onInsert: (payload: InsertAssetPayload) => void; theme: CanvasTheme }) { +const CanvasAssetsTab = memo(function CanvasAssetsTab({ onInsert, theme }: { onInsert: (payload: InsertAssetPayload) => void; theme: CanvasTheme }) { const { message } = App.useApp(); const assets = useAssetStore((state) => state.assets); const addAsset = useAssetStore((state) => state.addAsset); @@ -398,7 +398,7 @@ function CanvasAssetsTab({ onInsert, theme }: { onInsert: (payload: InsertAssetP ); -} +}); function AssetCard({ asset, theme, onInsert, onRemove }: { asset: Asset; theme: CanvasTheme; onInsert: () => void; onRemove: () => void }) { return ( @@ -440,7 +440,7 @@ function AssetCover({ asset }: { asset: Asset }) { // 提示词库 Tab —— 按来源折叠分组,展开时按需加载,点击复制 / 插入文本节点 // --------------------------------------------------------------------------- -function CanvasPromptsTab({ onInsert, theme }: { onInsert: (payload: InsertAssetPayload) => void; theme: CanvasTheme }) { +const CanvasPromptsTab = memo(function CanvasPromptsTab({ onInsert, theme }: { onInsert: (payload: InsertAssetPayload) => void; theme: CanvasTheme }) { const { message } = App.useApp(); const sources = usePromptSourceStore((state) => state.sources); const enabledSources = useMemo(() => sources.filter((source) => source.enabled), [sources]); @@ -486,7 +486,7 @@ function CanvasPromptsTab({ onInsert, theme }: { onInsert: (payload: InsertAsset setDetail(null)} onCopy={(prompt) => void copyPrompt(prompt)} /> ); -} +}); function PromptSourceGroup({ sourceId,