X6 图编辑引擎代码生成技能
核心约束(必须遵守)
<!-- CONSTRAINTS:START -->X6 3.x 关键约束(强制)
graph.render()不存在:X6 3.x 中new Graph()/addNode/addEdge/fromJSON均自动渲染,代码中不得出现graph.render()。- 不得声明
container变量:运行环境会作为函数参数注入container。Graph 初始化只使用字符串字面量container: 'container',禁止const/let/var container = ...,也禁止document.getElementById('container')。 - 使用插件方法前必须先
graph.use(new Plugin(...))注册对应插件:graph.toPNG / toSVG / toJPEG依赖Export;graph.select / unselect依赖Selection;graph.undo / redo依赖History;graph.copy / paste / cut依赖Clipboard;graph.bindKey依赖Keyboard。未注册插件时对应方法不存在。 - 自定义 shape 必须先注册再使用:
Graph.registerNode(name, def)/Graph.registerEdge(name, def)/Shape.HTML.register({ shape, ... })必须在首次addNode / addEdge之前完成。 @antv/x6仅导出 11 个插件类:Clipboard、Dnd、Export、History、Keyboard、MiniMap、Scroller、Selection、Snapline、Stencil、Transform。mousewheel、embedding、panning、connecting、translating、interacting、background、grid是new Graph()的构造选项,不是插件,不得 import 同名类、不得graph.use(new XxxClass())。例:滚轮缩放写在 Graph 构造中mousewheel: { enabled: true, zoomAtMousePosition: true, modifiers: ['ctrl'] }。- 节点/边动画使用
cell.animate(keyframes, options)(Web Animations API 风格):X6 3.x 没有node.transition(path, target, options)方法。源码中transition仅作为node.translate(tx, ty, { transition })/node.rotate(deg, { transition })的 options 字段存在(boolean | KeyframeEffectOptions),并非独立方法。示例:
多步属性变更可用// 通用动画 node.animate( { fill: ['#fff', '#1890ff'], transform: ['scale(1)', 'scale(1.2)'] }, { duration: 500, iterations: 1, fill: 'forwards' }, ); // 仅平移过渡 node.translate(120, 0, { transition: { duration: 500, easing: 'ease-in-out' } });graph.startBatch('animate'); cell.attr(...); graph.stopBatch('animate');包装。
初始化规范
container参数必填,必须使用字符串形式container: 'container',运行时环境会自动解析为 DOM 元素- 必须设置背景色:
background: { color: '#F2F7FA' },所有画布都需要统一的浅蓝灰色背景 - 不要添加
grid配置,除非用户明确要求显示网格 - 不要设置
width/height,除非用户明确指定画布尺寸;画布默认自适应容器大小 - 导入方式:
import { Graph } from '@antv/x6',仅导入实际使用到的类 - 禁止无条件导入
Shape:仅在使用Shape.HTML.register()等 Shape 静态方法时才导入Shape - 插件从
'@antv/x6'直接导入,如import { Graph, Selection, History } from '@antv/x6' - 禁止使用
@antv/x6-plugin-xxx独立包导入(已废弃) - 标准初始化模板:
import { Graph } from '@antv/x6'; const graph = new Graph({ container: 'container', background: { color: '#F2F7FA' }, });
节点操作规范
- 优先使用
graph.addNode()逐个添加节点,而非graph.fromJSON()批量导入(除非用户明确要求批量加载数据) - 内置 shape:
'rect'、'circle'、'ellipse'、'polygon'、'polyline'、'path'、'text'、'text-block'、'image'、'html' - 节点样式通过
attrs配置,遵循 SVG 属性命名 - 节点位置通过
x、y设置(左上角坐标),尺寸通过width、height设置 - 默认节点样式(除非用户指定其他样式,所有节点统一使用此默认样式):
attrs: { body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff', rx: 6, ry: 6 }, } - 禁止在 attrs 中使用 CSS 属性名(如
background-color),必须用 SVG 属性(如fill)
边操作规范
- 使用
graph.addEdge({ source, target, ... })添加边 source/target可以是:节点实例、节点 ID 字符串、{ cell: node, port: 'portId' }对象、坐标{ x, y }- 边样式:
attrs: { line: { stroke, strokeWidth, strokeDasharray, targetMarker, sourceMarker } } - 默认边样式(除非用户指定其他样式):
attrs: { line: { stroke: '#8f8f8f', strokeWidth: 1 } } - 箭头:
targetMarker: 'classic'(经典箭头)、'block'、'circle'、'diamond' - 路由器:
router: 'orth'(正交)、'manhattan'、'metro'、'er' - 连接器:
connector: 'rounded'(圆角)、'smooth'(贝塞尔曲线)、'jumpover'
连接桩(Ports)规范
- 连接桩定义在节点配置的
ports字段中 - 端口组:
ports: { groups: { groupName: { position, attrs, ... } }, items: [{ id, group }] } - position 取值:
'top'、'bottom'、'left'、'right' - 端口是连线的锚点,设置
attrs: { circle: { magnet: true } }允许从端口拖出连线 - 必须设置
magnet: true才能从该端口发起或接收连线
交互配置规范
- 连线交互在 Graph 配置中通过
connecting字段设置 connecting: { allowBlank: false, router: 'orth', connector: 'rounded', createEdge() {...} }- 节点移动限制:
translating: { restrict: true }或传函数限制区域 - 嵌入:
embedding: { enabled: true }允许节点拖入分组
插件使用规范
- 插件从
@antv/x6导入,通过graph.use(new Plugin(options))注册 - 可用插件:
Selection、Snapline、History、Clipboard、Keyboard、Scroller、MiniMap、Transform、Export、Stencil、Dnd - Selection:
graph.use(new Selection({ enabled: true, rubberband: true })) - Snapline:
graph.use(new Snapline({ enabled: true })) - History:
graph.use(new History({ enabled: true })) - Clipboard:
graph.use(new Clipboard({ enabled: true })) - Keyboard:
graph.use(new Keyboard({ enabled: true })) - Scroller:
graph.use(new Scroller({ enabled: true })) - MiniMap:
graph.use(new MiniMap({ enabled: true, container: minimapContainer })) - Transform:
graph.use(new Transform({ resizing: { enabled: true }, rotating: { enabled: true } })) - Export:
graph.use(new Export())(注册后可调用graph.toPNG()/graph.toSVG()) - 动态控制:
graph.enablePlugins('selection')/graph.disablePlugins('selection') - 禁止在 Graph 构造函数中直接传入
selecting、snapline等选项(3.x 不支持)
序列化规范
- 导出:
const data = graph.toJSON()返回{ cells: [...] }对象 - 导入:
graph.fromJSON(data)加载整个图数据 - 清空:
graph.clearCells()清除所有元素 - 禁止手动构造 cells 数组中的内部字段(如
zIndex、parent),应通过 API 操作
事件规范
- 节点事件:
graph.on('node:click', ({ node, e }) => {...}) - 边事件:
graph.on('edge:click', ({ edge, e }) => {...}) - 画布事件:
graph.on('blank:click', ({ e }) => {...}) - 变更事件:
graph.on('node:moved', ({ node }) => {...}) - 事件回调参数是对象,不是位置参数:
({ node, e })而非(node, e)
导入规范
- 所有使用到的类都必须出现在 import 语句中:如使用
Selection,必须import { Graph, Selection } from '@antv/x6' - 禁止使用
Graph.Selection、Graph.Keyboard等命名空间写法(不存在) - 禁止使用未导入的类:
new Selection(...)必须对应import { Selection } from '@antv/x6' - import 自检清单(强制,输出代码前必须逐行核对):对代码中每一个
new XxxYyy(...)调用,XxxYyy必须字面出现在第一行import { ..., XxxYyy } from '@antv/x6'的花括号内。常见漏写:Selection、Keyboard、History、Clipboard、Snapline、MiniMap、Transform、Scroller、Export、Stencil、Dnd、Shape(用到Shape.HTML.register时)。 - import 漏写为何会变成
Illegal constructor等运行时错:评测/Playground 环境用 UMD 构建(window.X6)执行代码,而不是真正的 ES Module。Selection、Keyboard等会根据 import 列表从window.X6解构出来;如果 import 漏写,标识符Selection会回退到window.Selection(浏览器原生 Selection 接口),new Selection({...})会抛Failed to construct 'Selection': Illegal constructor。Keyboard/History等同理(会is not a constructor)。 - import 漏写的标准修法:把所有用到的插件类合并到同一行
import { Graph, Selection, Keyboard, History, ... } from '@antv/x6';,不要分多行 import,也不要遗漏。
节点工具(Tools)规范
- 添加工具:
node.addTools([{ name: 'button-remove', args: { x: 0, y: 0 } }])或graph.addTools(node, [...]) - 移除工具:
node.removeTools()或graph.removeTools(node) - 检查工具:
node.hasTools() - 禁止使用
node.hideTools()/node.showTools()(3.x 不存在此 API) - 悬停显示/隐藏工具的正确方式:
graph.on('node:mouseenter', ({ node }) => { node.addTools([{ name: 'button-remove', args: { x: 0, y: 0 } }]); }); graph.on('node:mouseleave', ({ node }) => { node.removeTools(); });
渐变色规范
- X6 attrs 中
fill支持渐变对象语法,禁止直接操作graph.defs或document.createElementNS创建 SVG 渐变 - 线性渐变正确写法:
attrs: { body: { fill: { type: 'linearGradient', stops: [ { offset: '0%', color: '#0000ff' }, { offset: '100%', color: '#00ff00' }, ], }, }, }
代码输出规范
- 必须输出纯 JavaScript,禁止使用 TypeScript 语法(如
private、类型注解: string、as类型断言) - HTML 自定义节点使用
Shape.HTML.register({ shape, html, effect })注册自定义 shape,禁止使用class extends Node方式 - 禁止
Graph.registerHTMLComponent(name, factory)—— 这是 X6 2.x 旧 API,3.x 源码已无此方法。所有 HTML 节点统一通过Shape.HTML.register注册(详见references/core/x6-core-html-shape.md) effect数组指定哪些属性变化时触发重新渲染(如['data']);纯静态展示节点不要加 effect- HTML 节点正确写法:
import { Graph, Shape } from '@antv/x6'; Shape.HTML.register({ shape: 'my-html', effect: ['data'], html(node) { const div = document.createElement('div'); div.style.width = '100%'; div.style.height = '100%'; div.innerHTML = node.getData().content || ''; return div; }, }); const graph = new Graph({ container: 'container' }); graph.addNode({ shape: 'my-html', x: 100, y: 100, width: 200, height: 80, data: { content: '<div>Hello</div>' } });
Stencil 插件规范
- Stencil 通过
graph.use(new Stencil({ target: graph, groups: [...] }))注册 - 注册后通过
graph.getPlugin('stencil')获取实例,将stencil.container挂载到 DOM - Stencil 内的节点模板使用
graph.createNode(...)创建(非graph.addNode),再通过stencil.load(nodes, groupName)加载
动态端口规范
- 使用
node.addPort()动态添加端口时,必须在节点初始化时预定义对应的 ports.groups - 如果没有预定义 group,端口无法正确定位,可能导致渲染异常
- 正确写法:
const node = graph.addNode({ ..., ports: { groups: { in: { position: 'left', attrs: { circle: { r: 4, magnet: true, stroke: '#8f8f8f', fill: '#fff' } } }, out: { position: 'right', attrs: { circle: { r: 4, magnet: true, stroke: '#8f8f8f', fill: '#fff' } } }, }, }, }); node.addPort({ id: 'port1', group: 'out' }); registerNode+addNode的 ports 合并陷阱(强约束):Graph.registerNode(name, { ports: { items: [{ id: 'in1', group: 'in' }] } })之后,如果再graph.addNode({ shape: name, ports: { items: [{ id: 'in1', group: 'in' }] } }),X6 内部Cell构造时ObjectExt.merge(defaults, metadata)会按数组下标合并、node.addPorts走的也是[...current, ...new]简单拼接,不会去重,运行时直接抛Error: Duplicitied port id.。正确做法二选一:- 在
registerNode里只声明ports.groups,把ports.items留给addNode/ 后续node.addPort提供; - 或在
registerNode里完整声明ports.items,addNode时不再传ports.items(如需追加端口,调用node.addPort({ id: '新id', group: 'xxx' }),且新 id 不能与 registry 里已声明的重名)。
- 在
DOM/CSS 操作规范(HTML 节点 / Stencil / 自定义工具)
- HTML 节点
html(node)回调里给 DOM 设样式时,禁止直接写连字符属性:el.style.box-sizing = '...'、el.style.font-size = '...'会被 JS 解析为el.style.box - sizing = ...,抛Invalid left-hand side in assignment。正确写法二选一:- 驼峰:
el.style.boxSizing = 'border-box'、el.style.fontSize = '14px'、el.style.backgroundColor = '#fff'; - 方括号:
el.style['box-sizing'] = 'border-box'、el.style['font-size'] = '14px'; - 多条样式优先用
el.style.cssText = 'box-sizing:border-box;font-size:14px;'或Object.assign(el.style, { boxSizing: 'border-box', fontSize: '14px' })。
- 驼峰:
- 同理,
el.classList.add('...')/el.setAttribute('data-x', '...')是合法 API;禁止el.class = '...'/el['class-name'] = ...。
不存在的 API(禁止使用)
- 禁止
graph.scrollToCell()→ 正确方式:graph.centerCell(cell)滚动并居中到指定 cell - 禁止
graph.highlightCell()/graph.highlightNode()→ 正确方式:通过node.attr('body/stroke', '#f00')或添加 CSS class 实现高亮 - 禁止
Shape.Cylinder/Shape.Diamond等不存在的内置 Shape → 用'rect'+rx/ry或'polygon'自定义 - 禁止
Shape.Edge.define()/Shape.Node.define()→ 正确方式:Graph.registerEdge()/Graph.registerNode() - 禁止
Shape.Group/Shape.Group.define()/new Shape.Group()→ X6 3.x 的Shape命名空间没有Group导出(实际只有Circle / Edge / Ellipse / HTML / Image / Path / Polygon / Polyline / Rect / TextBlock,运行时会报Cannot read properties of undefined (reading 'define'))。父子分组的正确方式:直接graph.addNode({ shape: 'rect', ... })创建一个普通节点作为父节点,再通过parent.addChild(child)/parent.embed(child)建立父子关系;或用Graph.registerNode('my-group', { inherit: 'rect', markup: [...], attrs: {...} })注册一个自定义分组形状再addNode({ shape: 'my-group' })。 - 禁止 把
Embedding当插件 import / new /graph.use(new Embedding(...))→ X6 3.x 没有Embedding插件类(运行时会报Embedding is not a constructor)。节点嵌入是 Graph 构造选项:new Graph({ container, embedding: { enabled: true, findParent: 'bbox', frontOnly: false, validate: ({ child, parent }) => true } })。Hover 高亮通过highlighting.embedding配置,嵌入/解除嵌入事件为node:embedding/node:embedded。 - 禁止
history.batch()→ 正确方式:graph.startBatch('custom'); ...; graph.stopBatch('custom');或graph.batchUpdate(() => { ... }) - 禁止
graph.defs/graph.svgDoc/document.createElementNS('...', 'linearGradient' | 'defs' | 'marker')→ X6 3.x 不暴露graph.defs/graph.svgDoc,运行时会报Cannot read properties of undefined。正确方式:- 节点/边普通
fill渐变:直接用 attrs 中的渐变对象语法(fill: { type: 'linearGradient', stops: [...] }) - 自定义 marker 需要渐变填充:先
const id = graph.defineGradient({ type: 'linearGradient', stops: [{ offset: 0, color: '#f00' }, { offset: 1, color: '#0f0' }] }),再在 marker 对象里写fill: \url(#${id})`` - 自定义 marker / filter 同理使用
graph.defineMarker(options)/graph.defineFilter(options)
- 节点/边普通
渲染输出规范(必须遵守)
- 画布初始化后必须存在至少一个
graph.addNode/graph.addEdge/graph.fromJSON调用,确保画布有可视内容。即使用户 query 只描述了交互(panning / mousewheel / 插件等)配置,也必须自行补 2~3 个示例节点 + 1 条边作为渲染载体,否则视觉验证会被判定为「白屏」。 - 所有节点/边添加完成后,必须在末尾调用
graph.centerContent()(或在画布需要随内容缩放时使用graph.zoomToFit({ padding: 20, maxScale: 1 }))。X6 默认不会自动居中,缺失该调用会导致内容偏向左上角、视觉评分不通过。两者二选一,不可同时调用。 - 多个交互(
panning+mousewheel+Selectionrubberband)同时启用时,必须用modifiers错开触发条件(例如:panning 用'shift',mousewheel 用'ctrl',rubberband 留空)。禁止把'mouseWheel'放进panning.eventTypes同时又启用mousewheel,两者会争抢滚轮事件。
禁止的错误模式
❌ 使用已废弃的独立插件包
// 错误:独立插件包已废弃
import { Selection } from '@antv/x6-plugin-selection';
import { History } from '@antv/x6-plugin-history';
// 正确:从 @antv/x6 直接导入
import { Graph, Selection, History } from '@antv/x6';
const graph = new Graph({ container: 'container' });
graph.use(new Selection({ enabled: true, rubberband: true }));
graph.use(new History({ enabled: true }));
❌ 在构造函数中传入插件选项
// 错误:3.x 不支持构造函数选项模式
const graph = new Graph({
container: 'container',
selecting: { enabled: true }, // ❌
snapline: { enabled: true }, // ❌
history: { enabled: true }, // ❌
});
// 正确:使用 graph.use() 注册插件
import { Graph, Selection, Snapline, History } from '@antv/x6';
const graph = new Graph({ container: 'container' });
graph.use(new Selection({ enabled: true }));
graph.use(new Snapline({ enabled: true }));
graph.use(new History({ enabled: true }));
❌ 混淆 CSS 属性和 SVG 属性
// 错误:使用 CSS 属性名
attrs: {
body: {
'background-color': '#fff', // ❌
'border-radius': '6px', // ❌
}
}
// 正确:使用 SVG 属性名
attrs: {
body: {
fill: '#fff', // ✅ 背景色
rx: 6, // ✅ 圆角
ry: 6,
stroke: '#8f8f8f', // ✅ 边框色
strokeWidth: 1, // ✅ 边框宽度
}
}
❌ 缺少 container
// 错误:遗漏 container
const graph = new Graph({});
// 正确:container 必填
const graph = new Graph({ container: 'container' });
❌ 连接桩未设置 magnet
// 错误:端口无法连线
ports: {
items: [{ id: 'port1', group: 'out' }],
groups: {
out: { position: 'right', attrs: { circle: { r: 5 } } }
}
}
// 正确:设置 magnet: true
ports: {
items: [{ id: 'port1', group: 'out' }],
groups: {
out: { position: 'right', attrs: { circle: { r: 5, magnet: true, stroke: '#8f8f8f' } } }
}
}
❌ 事件回调使用位置参数
// 错误:参数不是位置传递
graph.on('node:click', (node, e) => { ... });
// 正确:解构对象参数
graph.on('node:click', ({ node, e }) => { ... });
基础结构模板
import { Graph } from '@antv/x6';
const graph = new Graph({
container: 'container',
background: { color: '#F2F7FA' },
});
const source = graph.addNode({
shape: 'rect',
x: 40,
y: 40,
width: 100,
height: 40,
label: 'Source',
attrs: {
body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff', rx: 6, ry: 6 },
},
});
const target = graph.addNode({
shape: 'rect',
x: 300,
y: 200,
width: 100,
height: 40,
label: 'Target',
attrs: {
body: { stroke: '#8f8f8f', strokeWidth: 1, fill: '#fff', rx: 6, ry: 6 },
},
});
graph.addEdge({
source,
target,
attrs: {
line: { stroke: '#8f8f8f', strokeWidth: 1 },
},
});
// 内容居中:所有节点/边添加完成后调用,使画布内容相对于容器居中显示
// 如需缩放以适应容器,使用 graph.zoomToFit({ padding: 20, maxScale: 1 }) 替代
graph.centerContent();
场景选择指南
| 场景 | 推荐配置 | 关键特性 | |------|----------|----------| | DAG 数据管道 | ports + orth router + connecting | 有向无环、端口连线 | | ER 实体关系图 | HTML 节点 + er router | 表格式节点、字段展示 | | 流程图/审批流 | 菱形判断节点 + 分支边 | 条件分支、多路径 | | 组织架构图 | orth router + 树形布局 | 层级关系、折叠 | | 血缘分析 | 左右布局 + smooth connector | 多层流转、端口 | | 网络拓扑 | 圆形节点 + 星型结构 | 设备类型、连接状态 | | 状态机 | 圆形节点 + 边标签 | 状态转换、事件触发 |
内置节点类型
| shape | 形状 | 适用场景 |
|-------|------|----------|
| rect | 矩形 | 通用节点、流程步骤 |
| circle | 圆形 | 状态节点、端点 |
| ellipse | 椭圆 | 通用强调 |
| polygon | 多边形 | 菱形(判断)、六边形 |
| text | 纯文本 | 标注、注释 |
| image | 图片 | 图标节点 |
| html | HTML | 富文本、表格式节点 |
路由器与连接器
路由器(Router)— 决定边的路径走向
| 类型 | 效果 | 适用场景 |
|------|------|----------|
| normal | 直线(默认) | 简单图 |
| orth | 正交折线 | 流程图、DAG |
| manhattan | 智能正交(绕障) | 复杂布局 |
| metro | 地铁线风格 | 地铁图 |
| er | ER 图专用 | 实体关系图 |
连接器(Connector)— 决定边的线条样式
| 类型 | 效果 | 适用场景 |
|------|------|----------|
| normal | 直线段(默认) | 简单图 |
| rounded | 圆角折线 | 流程图(推荐) |
| smooth | 贝塞尔曲线 | 血缘图、关系图 |
| jumpover | 跨线跳跃 | 复杂交叉 |
插件速查
| 插件 | 注册方式 | 功能 |
|------|----------|------|
| Selection | graph.use(new Selection({ enabled: true, rubberband: true })) | 框选节点 |
| Snapline | graph.use(new Snapline({ enabled: true })) | 对齐辅助线 |
| History | graph.use(new History({ enabled: true })) | 撤销/重做 |
| Clipboard | graph.use(new Clipboard({ enabled: true })) | 复制/粘贴 |
| Keyboard | graph.use(new Keyboard({ enabled: true })) | 快捷键绑定 |
| Scroller | graph.use(new Scroller({ enabled: true })) | 滚动画布 |
| MiniMap | graph.use(new MiniMap({ enabled: true, container })) | 小地图导航 |
| Transform | graph.use(new Transform({ resizing: { enabled: true }, rotating: { enabled: true } })) | 节点缩放/旋转 |
| Export | graph.use(new Export()) | 导出 PNG/SVG |
| Stencil | graph.use(new Stencil({ target: graph, groups: [...] })) | 侧边栏拖拽面板 |
| Dnd | graph.use(new Dnd({ target: graph })) | 拖拽创建节点 |