VueFlow 图自适应容器尺寸教程

适用场景:VueFlow 图表需要在容器宽高动态变化时(如面板折叠、窗口 resize、布局变化)自动缩放适配。


核心思路

ResizeObserver 监听容器 div 的尺寸变化,每次变化时调用 VueFlow 的 fitView() 重新适配视口。

scss 复制代码
容器尺寸变化 → ResizeObserver 触发 → nextTick → fitView() → [可选] panBy() 偏移补偿

完整实现

第一步:补全 imports

javascript 复制代码
import { ref, onMounted, markRaw, nextTick, onBeforeUnmount } from "vue";
import { VueFlow, useVueFlow } from "@vue-flow/core";
//                 ↑ 必须引入 useVueFlow

第二步:声明变量

csharp 复制代码
// 容器 ref
const containerRef = ref<HTMLElement | null>(null);

// ResizeObserver 实例(保存引用,方便销毁时清理)
let resizeObserver: ResizeObserver | null = null;

// 从 useVueFlow 解构需要的 API
const { fitView, panBy } = useVueFlow();
// 如果不需要偏移补偿,只解构 fitView 即可:
// const { fitView } = useVueFlow();

第三步:fitView 适配函数

基础版(无偏移补偿):

scss 复制代码
function fitViewWithOffset() {
  nextTick(() => {
    fitView();
  });
}

带偏移补偿版(适用于有视觉偏移需求的场景,如台体底部对齐):

scss 复制代码
function fitViewWithOffset() {
  nextTick(() => {
    fitView();
    nextTick(() => {
      panBy({ x: 0, y: 150 }); // 数值按实际视觉效果调整
    });
  });
}

为什么需要双层 nextTick?

  • 第一层:等容器 DOM 更新完毕,VueFlow 感知到新尺寸
  • 第二层:等 fitView() 的视口计算完成,再执行 panBy()

第四步:onMounted 注册监听

scss 复制代码
onMounted(() => {
  if (containerRef.value) {
    resizeObserver = new ResizeObserver(() => {
      fitViewWithOffset();
    });
    resizeObserver.observe(containerRef.value); // 监听容器 div
  }
});

第五步:onBeforeUnmount 清理

ini 复制代码
onBeforeUnmount(() => {
  resizeObserver?.disconnect(); // 断开监听
  resizeObserver = null;        // 释放引用,防止内存泄漏
});

第六步:初始加载适配(onPaneReady)

scss 复制代码
// 首次加载用 @pane-ready 事件,逻辑与 ResizeObserver 回调保持一致
function onPaneReady(instance: VueFlowStore) {
  instance.fitView();
  nextTick(() => {
    instance.panBy({ x: 0, y: 150 }); // 如有偏移需求则加,否则省略
  });
}

第七步:模板绑定 ref

xml 复制代码
<!-- 容器 div 必须绑定 ref,ResizeObserver 才能监听到它 -->
<div ref="containerRef" class="your-flow-container">
  <VueFlow
    v-model:nodes="nodes"
    v-model:edges="edges"
    :node-types="nodeTypes"
    :edge-types="edgeTypes"
    @pane-ready="onPaneReady"
  />
</div>

完整模板(可直接复制)

xml 复制代码
<script setup lang="ts">
import { ref, onMounted, markRaw, nextTick, onBeforeUnmount } from "vue";
import { VueFlow, useVueFlow } from "@vue-flow/core";
import type { Edge, Node, VueFlowStore } from "@vue-flow/core";
import "@vue-flow/core/dist/style.css";
import "@vue-flow/core/dist/theme-default.css";

// --- 自适应相关 ---
const containerRef = ref<HTMLElement | null>(null);
let resizeObserver: ResizeObserver | null = null;
const { fitView } = useVueFlow(); // 有偏移需求时加 panBy

function fitViewWithOffset() {
  nextTick(() => {
    fitView();
    // 如需偏移补偿,再嵌套一层 nextTick:
    // nextTick(() => { panBy({ x: 0, y: 150 }); });
  });
}

function onPaneReady(instance: VueFlowStore) {
  instance.fitView();
  // 如需偏移补偿:
  // nextTick(() => { instance.panBy({ x: 0, y: 150 }); });
}

onMounted(() => {
  if (containerRef.value) {
    resizeObserver = new ResizeObserver(() => {
      fitViewWithOffset();
    });
    resizeObserver.observe(containerRef.value);
  }
});

onBeforeUnmount(() => {
  resizeObserver?.disconnect();
  resizeObserver = null;
});

// --- 节点 / 边 ---
const nodes = ref<Node[]>([/* ... */]);
const edges = ref<Edge[]>([/* ... */]);
const nodeTypes = { /* ... */ };
const edgeTypes = { /* ... */ };
</script>

<template>
  <div ref="containerRef" class="flow-container">
    <VueFlow
      v-model:nodes="nodes"
      v-model:edges="edges"
      :node-types="nodeTypes"
      :edge-types="edgeTypes"
      @pane-ready="onPaneReady"
    />
  </div>
</template>

<style scoped>
.flow-container {
  position: relative;
  width: 100%;
  height: 100%;
}
</style>

知识点速查

知识点 说明
ResizeObserver 浏览器原生 API,监听 DOM 元素的尺寸变化
fitView() VueFlow API,将所有节点缩放平移至充满视口
panBy({ x, y }) VueFlow API,在当前视口基础上做相对偏移
useVueFlow() VueFlow composable,在 <script setup> 中获取 fitView / panBy 等方法
onPaneReady VueFlow 事件,首次渲染完成时触发,用于初始适配
双层 nextTick 第一层等 DOM 更新,第二层等 fitView 计算完成
resizeObserver?.disconnect() 组件销毁时必须调用,防止内存泄漏

常见问题

Q: fitView 后图位置不对?fitView() 之后嵌套一层 nextTick 再调用 panBy() 做补偿。

Q: 容器初始化时图没有适配? 确保绑定了 @pane-ready="onPaneReady",VueFlow 内部初始化完成后才能调用 fitView。

Q: ResizeObserver 触发太频繁性能差? 可以对 fitViewWithOffset 加防抖(debounce),延迟 100~200ms 执行,减少高频调用。

scss 复制代码
import { debounce } from 'lodash-es';

const debouncedFitView = debounce(() => {
  nextTick(() => { fitView(); });
}, 150);

// 替换 ResizeObserver 回调中的调用:
resizeObserver = new ResizeObserver(debouncedFitView);
相关推荐
发现一只大呆瓜2 小时前
超全 Vite 性能优化指南:网络、资源、预渲染三维落地方案
前端·面试·vite
IT_陈寒2 小时前
Vue的computed属性怎么突然不更新了?
前端·人工智能·后端
智商不够_熬夜来凑2 小时前
【Picker】单选多选
前端·javascript·vue.js
米饭不加菜3 小时前
Typora 原生流程图语法完全指南(Flowchart.js)
前端·javascript·流程图
scan7243 小时前
langgraphy条件边
前端·javascript·html
冰小忆3 小时前
类变量在继承场景下的初始化规则是怎样的?
java·前端·数据库
YAwu114 小时前
JavaScript this 底层机制剖析
前端·javascript
你好潘先生4 小时前
让 AI 任务不丢进度:YeeroAI 后台续跑与全局快捷操作实践
前端·人工智能·后端
小KK_4 小时前
写给前端小白:我终于搞懂了JS原型和原型链
前端·javascript