chore: Performance optimizations
# What's changed? * Remove keys * Stabilize props * Force reset store on unmount
This commit is contained in:
@@ -113,10 +113,14 @@ onMounted(() => {
|
||||
if (edge.value.targetX !== targetX) edge.value.targetX = targetX
|
||||
if (edge.value.targetY !== targetY) edge.value.targetY = targetY
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
{ immediate: true },
|
||||
)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
edge.value = null
|
||||
})
|
||||
|
||||
const slots = inject(Slots)
|
||||
|
||||
const name = ref(edge.value.type ?? 'default')
|
||||
|
||||
@@ -55,7 +55,7 @@ const onSelectNodeHandler = (event: MouseEvent) => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
useResizeObserver(nodeElement, () =>
|
||||
const observer = useResizeObserver(nodeElement, () =>
|
||||
store.updateNodeDimensions([{ id: node.value.id, nodeElement: nodeElement.value, forceUpdate: true }]),
|
||||
)
|
||||
|
||||
@@ -67,6 +67,8 @@ onMounted(() => {
|
||||
{ flush: 'post' },
|
||||
)
|
||||
|
||||
onBeforeUnmount(() => observer.stop())
|
||||
|
||||
store.updateNodeDimensions([{ id: node.value.id, nodeElement: nodeElement.value, forceUpdate: true }])
|
||||
})
|
||||
|
||||
@@ -91,9 +93,13 @@ watch(
|
||||
|
||||
node.value.handleBounds = getHandleBounds(nodeElement.value, scale.value)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
nodeElement.value = undefined
|
||||
node.value = null
|
||||
})
|
||||
|
||||
store.updateNodePosition({ id: node.value.id, diff: { x: 0, y: 0 } })
|
||||
|
||||
const slots = inject(Slots)
|
||||
|
||||
@@ -106,7 +106,8 @@ export default (options?: Options): UseVueFlow => {
|
||||
scope.vueFlowId = name
|
||||
|
||||
onScopeDispose(() => {
|
||||
storage.remove(name)
|
||||
vueFlow!.$reset()
|
||||
storage.remove(vueFlow!.id)
|
||||
vueFlow = null
|
||||
})
|
||||
}
|
||||
@@ -116,7 +117,9 @@ export default (options?: Options): UseVueFlow => {
|
||||
|
||||
if (!vueFlow) throw new Error('[vueflow]: store instance not found.')
|
||||
|
||||
if (scope) provide(VueFlow, vueFlow)
|
||||
if (scope) {
|
||||
provide(VueFlow, vueFlow)
|
||||
}
|
||||
|
||||
return vueFlow
|
||||
}
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
<script lang="ts" setup>
|
||||
import NodeWrapper from '../../components/Nodes/NodeWrapper.vue'
|
||||
import { useVueFlow } from '../../composables'
|
||||
import { GraphNode, SnapGrid } from '../../types'
|
||||
|
||||
const { store } = useVueFlow()
|
||||
interface Props {
|
||||
draggable?: boolean
|
||||
selectable?: boolean
|
||||
connectable?: boolean
|
||||
snapToGrid?: boolean
|
||||
snapGrid?: SnapGrid
|
||||
nodes: GraphNode[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const draggable = (d?: boolean) => (typeof d === 'undefined' ? props.draggable : d)
|
||||
const selectable = (s?: boolean) => (typeof s === 'undefined' ? props.selectable : s)
|
||||
const connectable = (c?: boolean) => (typeof c === 'undefined' ? props.connectable : c)
|
||||
const snapGrid = (sg?: SnapGrid) => (sg ?? props.snapToGrid ? props.snapGrid : undefined)
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
@@ -12,14 +26,14 @@ export default {
|
||||
<template>
|
||||
<div class="vue-flow__nodes vue-flow__container">
|
||||
<NodeWrapper
|
||||
v-for="node of store.getNodes"
|
||||
v-for="node of props.nodes"
|
||||
:id="node.id"
|
||||
:key="node.id"
|
||||
:node="node"
|
||||
:draggable="typeof node.draggable === 'undefined' ? store.nodesDraggable : !!node.draggable"
|
||||
:selectable="typeof node.selectable === 'undefined' ? store.elementsSelectable : !!node.selectable"
|
||||
:connectable="typeof node.connectable === 'undefined' ? store.nodesConnectable : !!node.connectable"
|
||||
:snap-grid="node.snapGrid ?? (store.snapToGrid ? store.snapGrid : undefined)"
|
||||
:draggable="draggable(node.draggable)"
|
||||
:selectable="selectable(node.selectable)"
|
||||
:connectable="connectable(node.connectable)"
|
||||
:snap-grid="snapGrid(node.snapGrid)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -53,13 +53,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<UserSelection v-if="selectionKeyPressed" :key="`user-selection-${id}`" />
|
||||
<NodesSelection v-if="store.nodesSelectionActive" :key="`nodes-selection-${id}`" />
|
||||
<div
|
||||
:key="`flow-pane-${id}`"
|
||||
class="vue-flow__pane vue-flow__container"
|
||||
@click="onClick"
|
||||
@contextmenu="onContextMenu"
|
||||
@wheel="onWheel"
|
||||
/>
|
||||
<UserSelection v-if="selectionKeyPressed" />
|
||||
<NodesSelection v-if="store.nodesSelectionActive" />
|
||||
<div class="vue-flow__pane vue-flow__container" @click="onClick" @contextmenu="onContextMenu" @wheel="onWheel" />
|
||||
</template>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useVueFlow, useZoomPanHelper, useWindow } from '../../composables'
|
||||
import { FlowExportObject, FlowInstance, Store, XYPosition } from '../../types'
|
||||
import { pointToRendererPoint } from '../../utils'
|
||||
|
||||
const { id, store } = useVueFlow()
|
||||
const { store } = useVueFlow()
|
||||
|
||||
const untilDimensions = async (store: Store) => {
|
||||
// if ssr we can't wait for dimensions, they'll never really exist
|
||||
@@ -55,9 +55,9 @@ onMounted(async () => {
|
||||
await untilDimensions(store)
|
||||
|
||||
ready.value = true
|
||||
store.instance = instance
|
||||
store.instance = instance as FlowInstance
|
||||
store.fitViewOnInit && instance.fitView()
|
||||
store.hooks.paneReady.trigger(instance)
|
||||
store.hooks.paneReady.trigger(instance as FlowInstance)
|
||||
})
|
||||
|
||||
const transform = computed(() => `translate(${store.viewport.x}px,${store.viewport.y}px) scale(${store.viewport.zoom})`)
|
||||
@@ -68,13 +68,16 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
:key="`transformation-pane-${id}`"
|
||||
class="vue-flow__transformationpane vue-flow__container"
|
||||
:style="{ transform, opacity: ready ? undefined : 0 }"
|
||||
>
|
||||
<NodeRenderer :key="`node-renderer-${id}`" />
|
||||
<EdgeRenderer :key="`edge-renderer-${id}`" />
|
||||
<div class="vue-flow__transformationpane vue-flow__container" :style="{ transform, opacity: ready ? undefined : 0 }">
|
||||
<NodeRenderer
|
||||
:draggable="store.nodesDraggable"
|
||||
:selectable="store.elementsSelectable"
|
||||
:connectable="store.nodesConnectable"
|
||||
:nodes="store.getNodes"
|
||||
:snap-to-grid="store.snapToGrid"
|
||||
:snap-grid="store.snapGrid"
|
||||
/>
|
||||
<EdgeRenderer />
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { clamp, clampPosition } from '../../utils'
|
||||
import SelectionPane from '../SelectionPane/SelectionPane.vue'
|
||||
import Transform from './Transform.vue'
|
||||
|
||||
const { id, store, zoomActivationKeyCode, selectionKeyCode } = useVueFlow()
|
||||
const { store, zoomActivationKeyCode, selectionKeyCode } = useVueFlow()
|
||||
const viewportEl = templateRef<HTMLDivElement>('viewport', null)
|
||||
|
||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
||||
@@ -83,7 +83,7 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
d3Selection
|
||||
?.on('wheel', (event: WheelEvent) => {
|
||||
.on('wheel', (event: WheelEvent) => {
|
||||
if (store.panOnScroll && !zoomKeyPressed.value) {
|
||||
if (isWrappedWithClass(event, store.noWheelClassName)) return
|
||||
event.preventDefault()
|
||||
@@ -108,7 +108,7 @@ onMounted(() => {
|
||||
const deltaY = store.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
||||
|
||||
if (d3Selection && store.panOnScrollSpeed)
|
||||
d3Zoom?.translateBy(
|
||||
d3Zoom.translateBy(
|
||||
d3Selection,
|
||||
-(deltaX / currentZoom) * store.panOnScrollSpeed,
|
||||
-(deltaY / currentZoom) * store.panOnScrollSpeed,
|
||||
@@ -163,10 +163,10 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div ref="viewport" :key="`viewport-${id}`" class="vue-flow__viewport vue-flow__container">
|
||||
<div ref="viewport" class="vue-flow__viewport vue-flow__container">
|
||||
<Transform>
|
||||
<slot />
|
||||
</Transform>
|
||||
<SelectionPane :key="`selection-pane-${id}`" />
|
||||
<SelectionPane />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -33,7 +33,10 @@ const modelProps = useVModels(props, emit)
|
||||
const { id, hooks, getNodeTypes, getEdgeTypes, ...rest } = useVueFlow()
|
||||
|
||||
const dispose = useWatch(modelProps, { id, hooks, getNodeTypes, getEdgeTypes, ...rest })
|
||||
onUnmounted(() => dispose())
|
||||
|
||||
onUnmounted(() => {
|
||||
dispose()
|
||||
})
|
||||
|
||||
useHooks(emit, hooks.value)
|
||||
|
||||
@@ -46,14 +49,14 @@ export default {
|
||||
</script>
|
||||
<template>
|
||||
<div class="vue-flow">
|
||||
<Viewport :key="`renderer-${id}`">
|
||||
<Viewport>
|
||||
<template #nodes>
|
||||
<template v-for="nodeName of Object.keys(getNodeTypes)" :key="`node-${nodeName}-${id}`">
|
||||
<template v-for="nodeName of Object.keys(getNodeTypes)">
|
||||
<slot :name="`node-${nodeName}`" />
|
||||
</template>
|
||||
</template>
|
||||
<template #edges>
|
||||
<template v-for="edgeName of Object.keys(getEdgeTypes)" :key="`edge-${edgeName}-${id}`">
|
||||
<template v-for="edgeName of Object.keys(getEdgeTypes)">
|
||||
<slot :name="`edge-${edgeName}`" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -274,7 +274,7 @@ export default (state: State, getters: ComputedGetters): Actions => {
|
||||
const addEdges: Actions['addEdges'] = (params) => {
|
||||
const curr = params instanceof Function ? params(state.edges) : params
|
||||
|
||||
curr.forEach((param) => {
|
||||
curr.reduce<GraphEdge[]>((acc, param) => {
|
||||
const edge = addEdge(param, state.edges)
|
||||
if (edge) {
|
||||
const sourceNode = getters.getNode.value(edge.source)!
|
||||
@@ -284,16 +284,18 @@ export default (state: State, getters: ComputedGetters): Actions => {
|
||||
const missingTarget = !targetNode || typeof targetNode === 'undefined'
|
||||
if (missingSource) console.warn(`[vueflow]: Couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
|
||||
if (missingTarget) console.warn(`[vueflow]: Couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
|
||||
if (missingTarget || missingSource) return
|
||||
if (missingTarget || missingSource) return acc
|
||||
|
||||
state.edges.push({
|
||||
acc.push({
|
||||
...state.defaultEdgeOptions,
|
||||
...edge,
|
||||
sourceNode,
|
||||
targetNode,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return acc
|
||||
}, state.edges)
|
||||
}
|
||||
|
||||
const updateEdge: Actions['updateEdge'] = (oldEdge, newConnection) =>
|
||||
|
||||
Reference in New Issue
Block a user