From f4bf169b2fd9006f0e014f06e583375b93271f5b Mon Sep 17 00:00:00 2001 From: John Haugeland Date: Fri, 13 Mar 2026 05:55:52 -0700 Subject: [PATCH] perf: remove deep: true from 3 hot watchers to reduce reactivity overhead (#9248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All three watchers used { deep: true } unnecessarily because their watched sources already produce new object/array references on change, making deep traversal redundant: - GraphView.vue: queueStore.tasks is a computed that spreads three shallowRef arrays into a new array each time. TaskItemImpl instances are immutable (readonly fields, replaced not mutated). - DomWidget.vue: Replaced opaque widgetState deep watcher with explicit property deps (pos, size, zIndex, readonly, positionOverride). During 60 FPS pan/zoom, Vue no longer walks the entire DomWidgetState object graph including the markRaw widget — only 5 leaf properties are checked. pos and size are new array literals each frame; the rest are primitives. - GraphCanvas.vue: nodeLocationProgressStates is a computed returning a new Record on every execution progress event (nodeProgressStates is replaced wholesale via WebSocket handler). Co-Authored-By: Claude Opus 4.6 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9248-perf-remove-deep-true-from-3-hot-watchers-to-reduce-reactivity-overhead-3136d73d365081278f18da5a2eef6971) by [Unito](https://www.unito.io) Co-authored-by: bymyself --- src/components/graph/GraphCanvas.vue | 12 +++++++++--- src/views/GraphView.vue | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index d6cddc0407..92dc81ba9b 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -363,7 +363,14 @@ watch( } ) -// Update the progress of executing nodes +/** + * Propagates execution progress from the store to LiteGraph node objects + * and triggers a canvas redraw. + * + * No `deep: true` needed — `nodeLocationProgressStates` is a computed that + * returns a new `Record` object on every progress event (the underlying + * `nodeProgressStates` ref is replaced wholesale by the WebSocket handler). + */ watch( () => [executionStore.nodeLocationProgressStates, canvasStore.canvas] as const, @@ -381,8 +388,7 @@ watch( // Force canvas redraw to ensure progress updates are visible canvas.setDirty(true, false) - }, - { deep: true } + } ) // Update node slot errors for LiteGraph nodes diff --git a/src/views/GraphView.vue b/src/views/GraphView.vue index cc75b6c9a1..90eec04b7d 100644 --- a/src/views/GraphView.vue +++ b/src/views/GraphView.vue @@ -142,11 +142,18 @@ watch( { immediate: true } ) +/** + * Reports task completion telemetry to Electron analytics when tasks + * transition from running to history. + * + * No `deep: true` needed — `queueStore.tasks` is a computed that spreads + * three `shallowRef` arrays into a new array on every change, and + * `TaskItemImpl` instances are immutable (replaced, never mutated). + */ if (isDesktop) { watch( () => queueStore.tasks, (newTasks, oldTasks) => { - // Report tasks that previously running but are now completed (i.e. in history) const oldRunningTaskIds = new Set( oldTasks.filter((task) => task.isRunning).map((task) => task.jobId) ) @@ -161,8 +168,7 @@ if (isDesktop) { status: task.displayStatus.toLowerCase() }) }) - }, - { deep: true } + } ) }