chore: Performance optimizations

# What's changed?

* Remove keys
* Stabilize props
* Force reset store on unmount
This commit is contained in:
Braks
2022-04-24 13:34:22 +02:00
parent ad07b5b6ea
commit b6d8b57859
9 changed files with 73 additions and 44 deletions
+5 -1
View File
@@ -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')
+8 -2
View File
@@ -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)
+5 -2
View File
@@ -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>
+13 -10
View File
@@ -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>
+5 -5
View File
@@ -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>
+7 -4
View File
@@ -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>
+6 -4
View File
@@ -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) =>