perf: 优化智能体与工作流幕布渲染性能

- 分阶段加载智能体配置并按需缓存 MCP 工具

- 合并画布状态更新与节点尺寸监听,启用大图可视区域渲染和静态连线

- 隔离 Tinyflow Store 实例并补充数据同步与回归测试
This commit is contained in:
2026-07-27 18:27:01 +08:00
parent dc7e46260b
commit aedefe6b5e
39 changed files with 1349 additions and 408 deletions

View File

@@ -3,7 +3,9 @@
</template>
<script setup lang="ts">
import {Tinyflow as TinyflowNative, TinyflowOptions} from '@tinyflow-ai/ui';
import type { TinyflowOptions } from '@tinyflow-ai/ui';
import { Tinyflow as TinyflowNative } from '@tinyflow-ai/ui';
import '@tinyflow-ai/ui/dist/index.css';
import {
getCurrentInstance,
@@ -15,8 +17,7 @@ import {
watch,
} from 'vue';
type TinyflowDataOption = Exclude<TinyflowOptions['data'], string | undefined>;
type StructuredCloneFn = <T>(value: T) => T;
import { createTinyflowDataSync } from './dataSync';
const props = defineProps<
{
@@ -30,7 +31,7 @@ const attrs = useAttrs();
const instance = getCurrentInstance();
let tinyflow: TinyflowNative | null = null;
let mountedDataReady = false;
let lastAppliedDataSignature = '';
const dataSync = createTinyflowDataSync();
const optionalBooleanOptionKeys = new Set([
'readonly',
'hideBottomDock',
@@ -45,6 +46,7 @@ const optionalBooleanOptionKeys = new Set([
'nodesDraggable',
'nodesConnectable',
'elementsSelectable',
'onlyRenderVisibleElements',
'dropEnabled',
]);
@@ -81,58 +83,6 @@ function normalizeProps(source: Record<string, unknown>) {
);
}
// 安全深拷贝工具函数
function safeDeepClone<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') return obj;
try {
const clone = (globalThis as { structuredClone?: StructuredCloneFn })
.structuredClone;
if (clone) {
return clone(obj);
}
} catch {
// Fall through to JSON cloning for browsers without native structuredClone.
}
try {
return JSON.parse(JSON.stringify(obj));
} catch {
try {
console.warn(
'Failed to clone object, returning original (may cause issues)',
obj,
);
return obj;
} catch {
return obj;
}
}
}
function createDataSignature(data: unknown) {
if (data == null || typeof data === 'string') {
return String(data ?? '');
}
try {
return JSON.stringify(data);
} catch {
return '';
}
}
function cloneDataIfChanged(data: TinyflowOptions['data']) {
if (data == null || typeof data === 'string') {
return null;
}
const signature = createDataSignature(data);
if (signature && signature === lastAppliedDataSignature) {
return null;
}
lastAppliedDataSignature = signature;
return safeDeepClone(data as TinyflowDataOption);
}
onMounted(() => {
if (divRef.value) {
// 净化 props.data避免响应式对象或函数污染
@@ -141,8 +91,21 @@ onMounted(() => {
...normalizeProps(props),
} as any;
if ('data' in cleanedProps && cleanedProps.data != null) {
cleanedProps.data = cloneDataIfChanged(cleanedProps.data);
cleanedProps.data = dataSync.cloneExternalDataIfChanged(cleanedProps.data);
}
(['onDataChange', 'onDataCommit'] as const).forEach((callbackKey) => {
const callback = cleanedProps[callbackKey];
if (typeof callback !== 'function') {
return;
}
cleanedProps[callbackKey] = (
data: TinyflowOptions['data'],
...args: unknown[]
) => {
dataSync.markNativeData(data);
callback(data, ...args);
};
});
tinyflow = new TinyflowNative({
...cleanedProps,
@@ -158,7 +121,7 @@ onUnmounted(() => {
tinyflow = null;
}
mountedDataReady = false;
lastAppliedDataSignature = '';
dataSync.reset();
});
watch(
@@ -179,7 +142,7 @@ watch(
data != null &&
typeof data !== 'string'
) {
const clonedData = cloneDataIfChanged(data);
const clonedData = dataSync.cloneExternalDataIfChanged(data);
if (clonedData) {
tinyflow.updateData(clonedData, { preserveViewport: true });
}
@@ -229,7 +192,7 @@ const updateData = (
options?: { preserveViewport?: boolean },
) => {
if (tinyflow && data != null && typeof data !== 'string') {
const clonedData = cloneDataIfChanged(data);
const clonedData = dataSync.cloneExternalDataIfChanged(data);
if (!clonedData) {
return true;
}