应使用 svg 动态绘制依赖连线:通过 data-x/y 存坐标,用 和 箭头,配合 getscreenctm() 坐标转换;依赖需 js 显式建模与状态同步,避免伪元素或结构耦合。

用 <svg></svg> 动态画依赖连线,别碰 CSS 伪元素
纯 HTML 无法“可视化”依赖关系,所有箭头、路径、实时更新都得靠 JavaScript 驱动 <svg></svg>。CSS 的 ::before/::after 或 border + transform 拼箭头,会在节点位置变动、缩放、响应式重排时立刻错位,且无法绑定事件或做坐标计算。
正确做法是:每个组件节点加 data-x 和 data-y 属性存其可视区域中心坐标;<svg></svg> 全局覆盖在页面上;连线全部用 <line></line>,箭头定义在 <defs></defs> 里并用 marker-end 引用。
- 必须在
DOMContentLoaded后首次计算,之后监听resize和scroll(若节点可滚动)重新调用重绘函数 - 不能直接把
getBoundingClientRect().x塞进<line x1="..."></line>—— SVG 坐标系和文档流不一致,得用svg.getScreenCTM().inverse()转换 - 节点有
transform: translate()或 margin/padding?getBoundingClientRect()返回的是布局后坐标,可直接用,但父容器不能设overflow: hidden截断边界
依赖关系必须由 JS 显式建模,HTML 只负责声明
HTML 中的 data-prereq="component-a,component-b" 不是“自动生效”的指令,它只是个标记。真正起作用的是你写的协调逻辑:查 DOM、比对状态、更新类名或属性、触发重绘。
常见错误是把依赖写死在结构里,比如 <div id="step-2"><div id="step-1">,然后靠父子选择器控制显隐 —— 一旦组件动态插入、跨层级、或用 Web Components 封装,这种写法立刻失效。
<ul>
<li>用 <code>window.$deps = new Map() 集中存各组件当前状态(如 "auth-provider": "logged-in"),而非反复 document.querySelector()
connectedCallback 或 onload 里检查前置项是否就绪,不满足时用 requestIdleCallback 延迟重试,避免 DOM 还没挂载就查不到constructor 里访问 document 或其他组件 —— 此时 DOM 尚未 attach
<line></line> 的 stroke-linecap 和 markerUnits 是 IE11 兼容关键
IE11 对 SVG marker-end 渲染支持不全,尤其当线段极短或 markerUnits="strokeWidth" 时,箭头常消失。这不是 bug,是规范实现偏差。
绕过方式不是改用 <path></path> 手动画三角形(会破坏可维护性),而是用视觉补偿:
- 给
<line></line>加stroke-linecap="round",让末端变成圆点,至少保留“有连接”的语义 -
stroke-width设为1.5(非整数),IE11 对非整数描边更敏感,能触发 marker 渲染 - 若必须显示标准箭头,改用
markerUnits="userSpaceOnUse"并手动算refX/refY,但代价是失去缩放自适应
动态组件插入后,连线逻辑必须重绑定
用 innerHTML、insertAdjacentHTML 或框架(Vue/React)插入新组件时,data-prereq 属性虽在,但原先的连线逻辑不会自动识别它。你得显式触发一次依赖扫描和重绘。
不要每次插入都 querySelectorAll('[data-prereq]') 全量遍历 —— 性能差,且容易漏掉嵌套在 Shadow DOM 里的节点。
- 推荐在插入后 dispatch 一个
dependency:changed自定义事件,由全局协调器监听并只更新受影响的子图 - 对 Web Components,可在
attributeChangedCallback里监听data-prereq变化,触发局部重绘 - 若组件用
display: contents隐藏了无语义父节点,getBoundingClientRect()仍有效,但要确保该父节点没被overflow: hidden截断











