Appearance
节点简介
🌐 Introduction to Nodes
节点是图表的基本组成部分。它们可以是你想在图表中可视化的任何类型的数据,独立存在,并通过边相互连接以创建数据图。
🌐 Nodes are the underlying components of your graph. They can be any kind of data you want to visualize in your graph, existing independently and being interconnected through edges to create a data map.
请记住,每个节点都是独一无二的,因此需要一个唯一的ID以及一个XY位置。
🌐 Remember, every node is unique and thus requires a unique id and an XY-position.
有关节点可用选项的完整列表,请查看 节点接口。
🌐 For the full list of options available for a node, check out the Node Interface.
向图中添加节点
🌐 Adding Nodes to the Graph
节点通过传递给 Vue Flow 组件的 nodes 属性(或已弃用的 v-model 属性)来渲染。
🌐 Nodes are rendered by passing them to the nodes prop (or the deprecated v-model prop) of the Vue Flow component.
WARNING
此方法不会产生任何更改。有关更多信息,请查看受控流程部分。
vue
<script setup>
import { ref, onMounted } from 'vue'
import { VueFlow, Panel } from '@vue-flow/core'
const nodes = ref([
{
id: '1',
position: { x: 50, y: 50 },
data: { label: 'Node 1', },
}
]);
function addNode() {
const id = Date.now().toString()
nodes.value.push({
id,
position: { x: 150, y: 50 },
data: { label: `Node ${id}`, },
})
}
</script>
<template>
<VueFlow :nodes="nodes">
<Panel>
<button type="button" @click="addNode">Add a node</button>
</Panel>
</VueFlow>
</template>vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { Node } from '@vue-flow/core'
import { VueFlow, Panel } from '@vue-flow/core'
const nodes = ref<Node[]>([
{
id: '1',
position: { x: 50, y: 50 },
data: { label: 'Node 1', },
}
]);
function addNode() {
const id = Date.now().toString()
nodes.value.push({
id,
position: { x: 150, y: 50 },
data: { label: `Node ${id}`, },
})
}
</script>
<template>
<VueFlow :nodes="nodes">
<Panel>
<button type="button" @click="addNode">Add a node</button>
</Panel>
</VueFlow>
</template>如果你正在处理更复杂的图表,或者只是需要访问内部状态,useVueFlow 组合函数将非常有用。
🌐 If you are working with more complex graphs or simply require access to the internal state, the useVueFlow composable will come in handy.
通过 useVueFlow 可以使用 addNodes 操作,允许你直接向状态添加节点。
🌐 The addNodes action is available through useVueFlow, allowing you to add nodes straight to the state.
此外,这个操作不仅限于渲染图表的组件;它也可以在其他地方使用,比如侧边栏或工具栏。
🌐 What's more, this action isn't limited to the component rendering the graph; it can be utilized elsewhere, like in a Sidebar or Toolbar.
vue
<script setup>
import { ref } from 'vue'
import { Panel, VueFlow, useVueFlow } from '@vue-flow/core'
const initialNodes = ref([
{
id: '1',
position: { x: 50, y: 50 },
data: { label: 'Node 1' },
}
])
const { addNodes } = useVueFlow()
function generateRandomNode() {
return {
id: Math.random().toString(),
position: { x: Math.random() * 500, y: Math.random() * 500 },
label: 'Random Node',
}
}
function onAddNode() {
// add a single node to the graph
addNodes(generateRandomNode())
}
function onAddNodes() {
// add multiple nodes to the graph
addNodes(Array.from({ length: 10 }, generateRandomNode))
}
</script>
<template>
<VueFlow :nodes="initialNodes">
<Panel>
<button type="button" @click="onAddNode">Add a node</button>
<button type="button" @click="onAddNodes">Add multiple nodes</button>
</Panel>
</VueFlow>
</template>vue
<script setup lang="ts">
import { ref } from 'vue'
import type { Node } from '@vue-flow/core'
import { Panel, VueFlow, useVueFlow } from '@vue-flow/core'
const initialNodes = ref<Node[]>([
{
id: '1',
position: { x: 50, y: 50 },
data: { label: 'Node 1' },
}
])
const { addNodes } = useVueFlow()
function generateRandomNode() {
return {
id: Math.random().toString(),
position: { x: Math.random() * 500, y: Math.random() * 500 },
label: 'Random Node',
data: {
hello: 'world',
}
}
}
function onAddNode() {
// add a single node to the graph
addNodes(generateRandomNode())
}
function onAddNodes() {
// add multiple nodes to the graph
addNodes(Array.from({ length: 10 }, generateRandomNode))
}
</script>
<template>
<VueFlow :nodes="initialNodes">
<Panel>
<button type="button" @click="onAddNode">Add a node</button>
<button type="button" @click="onAddNodes">Add multiple nodes</button>
</Panel>
</VueFlow>
</template>INFO
如果你想实现节点的双向绑定,请使用 v-model:nodes="nodes"。这样你可以修改你的节点,并且这些更改会反映在图中。:::
从图形中移除节点
🌐 Removing Nodes from the Graph
与添加节点类似,可以通过从 mode-value(使用 v-model)或 Vue Flow 组件的 nodes 属性中移除节点来将节点从图中删除。
🌐 Similar to adding nodes, nodes can be removed from the graph by removing them from the mode-value (using v-model) or from the nodes prop of the Vue Flow component.
vue
<script setup>
import { ref } from 'vue'
import { VueFlow, Panel } from '@vue-flow/core'
const nodes = ref([
{
id: '1',
position: { x: 50, y: 50 },
data: { label: 'Node 1' },
},
{
id: '2',
position: { x: 150, y: 50 },
data: { label: 'Node 2' },
}
])
function removeNode(id) {
nodes.value = nodes.value.filter((node) => node.id !== id)
}
</script>
<template>
<VueFlow :nodes="nodes">
<Panel>
<button type="button" @click="removeNode('1')">Remove Node 1</button>
<button type="button" @click="removeNode('2')">Remove Node 2</button>
</Panel>
</VueFlow>
</template>通过 useVueFlow 可以使用 removeNodes 操作,允许你直接从状态中移除节点。
🌐 The removeNodes action is available through useVueFlow, allowing you to remove nodes straight from the state.
你还可以在渲染图表的组件外部使用此操作,例如在侧边栏或工具栏中。
🌐 You can also use this action outside the component rendering the graph, like in a Sidebar or Toolbar.
vue
<script setup>
import { ref } from 'vue'
import { VueFlow, Panel, useVueFlow } from '@vue-flow/core'
const initialNodes = ref([
{
id: '1',
position: { x: 50, y: 50 },
data: { label: 'Node 1' },
},
{
id: '2',
position: { x: 150, y: 50 },
data: { label: 'Node 2' },
}
])
const { removeNodes } = useVueFlow()
function removeOneNode() {
removeNodes('1')
}
function removeMultipleNodes() {
removeNodes(['1', '2'])
}
</script>
<template>
<VueFlow :nodes="initialNodes">
<Panel>
<button type="button" @click="removeOneNode">Remove Node 1</button>
<button type="button" @click="removeMultipleNodes">Remove Node 1 and 2</button>
</Panel>
</VueFlow>
</template>更新节点数据
🌐 Updating Node Data
由于节点是响应式对象,你可以在任何时候通过简单地更改它来更新其数据。这使你可以在任何时间禁用或启用句柄、更改标签,甚至向数据对象添加新属性。
🌐 Since nodes are reactive object, you can update their data at any point by simply mutating it. This allows you to disable or enable handles, change the label, or even add new properties to the data object at any point in time.
实现此目的的方法有很多,以下是一些示例:
🌐 There are multiple ways of achieving this, here are some examples:
ts
import { useVueFlow } from '@vue-flow/core'
const instance = useVueFlow()
// use the `updateNodeData` method to update the data of an node
instance.updateNodeData(nodeId, { hello: 'mona' })
// find the node in the state by its id
const node = instance.findNode(nodeId)
node.data = {
...node.data,
hello: 'world',
}
// you can also mutate properties like `selectable` or `draggable`
node.selectable = false
node.draggable = false
// or use `updateNode` to update the node directly
instance.updateNode(nodeId, { selectable: false, draggable: false })vue
<!-- CustomNode.vue -->
<script setup>
import { useNode } from '@vue-flow/core'
// `useNode` returns us the node object straight from the state
// since the node obj is reactive, we can mutate it to update our nodes' data
const { node } = useNode()
function onSomeEvent() {
node.data = {
...node.data,
hello: 'world',
}
// you can also mutate properties like `selectable` or `draggable`
node.selectable = false
node.draggable = false
}
</script>vue
<script setup>
import { ref } from 'vue'
const nodes = ref([
{
id: '1',
position: { x: 50, y: 50 },
data: {
label: 'Node 1',
hello: 'world',
}
},
])
function onSomeEvent(nodeId) {
const node = nodes.value.find((node) => node.id === nodeId)
node.data = {
...nodes.value[0].data,
hello: 'world',
}
// you can also mutate properties like `selectable` or `draggable`
node.selectable = false
node.draggable = false
}
</script>
<template>
<VueFlow :nodes="nodes">
<Panel>
<button type="button" @click="onSomeEvent('1')">Update Node 1</button>
</Panel>
</VueFlow>
</template>预定义节点类型
🌐 Predefined Node-Types
Vue Flow 提供了几种内置节点类型,你可以立即使用。包含的节点类型有 default、input 和 output。
🌐 Vue Flow provides several built-in node types that you can leverage immediately. The included node types are default, input, and output.
默认节点
🌐 Default Node
默认节点包含两个句柄,用作地图中的分支连接点。
🌐 A default node includes two handles and serves as a branching junction in your map.
你可以自由地在节点定义中确定句柄的位置。
🌐 You have the freedom to determine the location of handles in the node's definition.
ts
import { ref } from 'vue'
import { Position } from '@vue-flow/core'
const nodes = ref([
{
id: '1',
type: 'default', // You can omit this as it's the fallback type
targetPosition: Position.Top, // or Bottom, Left, Right,
sourcePosition: Position.Bottom, // or Top, Left, Right,
data: { label: 'Default Node' },
}
])输入节点
🌐 Input Node
输入节点有一个单一的连接点,默认位于底部。它表示你的地图的起点。
🌐 An input node features a single handle, which is by default positioned at the bottom. It represents a starting point of your map.
ts
import { ref } from 'vue'
import { Position } from '@vue-flow/core'
const nodes = ref([
{
id: '1',
type: 'input',
sourcePosition: Position.Bottom, // or Top, Left, Right,
data: { label: 'Input Node' },
}
])输出节点
🌐 Output Node
输出节点也有一个句柄,尽管它通常位于顶部。这个节点表示你地图的一个结参数。
🌐 An output node also possesses a single handle, although it is typically found at the top. This node represents a conclusion point of your map.
ts
import { ref } from 'vue'
import { Position } from '@vue-flow/core'
const nodes = ref([
{
id: '1',
type: 'output',
targetPosition: Position.Top, // or Bottom, Left, Right,
data: { label: 'Output Node' },
}
])用户定义节点
🌐 User-Defined Nodes
除了前面提到的默认节点类型之外,你可以根据需要创建任意数量的自定义节点类型。节点类型由你的节点定义决定。
🌐 On top of the default node types mentioned earlier, you can create as many custom node-types as you need. Node-types are determined from your nodes' definitions.
vue
<script setup>
import { ref } from 'vue'
import { VueFlow } from '@vue-flow/core'
import CustomNode from './CustomNode.vue'
import SpecialNode from './SpecialNode.vue'
export const nodes = ref([
{
id: '1',
data: { label: 'Node 1' },
// this will create the node-type `custom`
type: 'custom',
position: { x: 50, y: 50 },
},
{
id: '1',
data: { label: 'Node 1' },
// this will create the node-type `special`
type: 'special',
position: { x: 150, y: 50 },
}
])
</script>
<template>
<VueFlow :nodes="nodes">
<template #node-custom="customNodeProps">
<CustomNode v-bind="customNodeProps" />
</template>
<template #node-special="specialNodeProps">
<SpecialNode v-bind="specialNodeProps" />
</template>
</VueFlow>
</template>vue
<script setup>
import { Position, Handle } from '@vue-flow/core'
// props were passed from the slot using `v-bind="customNodeProps"`
const props = defineProps(['label'])
</script>
<template>
<div>
<Handle type="target" :position="Position.Top" />
<div>{{ label }}</div>
<Handle type="source" :position="Position.Bottom" />
</div>
</template>vue
<script setup lang="ts">
import { ref } from 'vue'
import type { Node } from '@vue-flow/core'
import { VueFlow } from '@vue-flow/core'
import CustomNode from './CustomNode.vue'
import SpecialNode from './SpecialNode.vue'
// You can pass 3 optional generic arguments to the Node interface, allowing you to define:
// 1. The data object type
// 2. The events object type
// 3. The possible node types
export interface CustomData {
hello: string
}
export interface CustomEvents {
onCustomEvent: (event: MouseEvent) => void
}
type CustomNodeTypes = 'custom' | 'special'
type CustomNode = Node<CustomData, CustomEvents, CustomNodeTypes>
export const nodes = ref<CustomNode[]>([
{
id: '1',
data: { label: 'Node 1' },
// this will create the node-type `custom`
type: 'custom',
position: { x: 50, y: 50 },
},
{
id: '2',
data: { label: 'Node 2' },
// this will create the node-type `special`
type: 'special',
position: { x: 150, y: 50 },
},
{
id: '3',
data: { label: 'Node 3' },
// this will throw a type error, as the type is not defined in the CustomEdgeTypes
// regardless it would be rendered as a default edge type
type: 'invalid',
position: { x: 150, y: 50 },
}
])
</script>
<template>
<VueFlow :nodes="nodes">
<template #node-custom="customNodeProps">
<CustomNode v-bind="customNodeProps" />
</template>
<template #node-special="specialNodeProps">
<SpecialNode v-bind="specialNodeProps" />
</template>
</VueFlow>
</template>vue
<script setup lang="ts">
import type { NodeProps } from '@vue-flow/core'
import { Position } from '@vue-flow/core'
import { CustomData, CustomEvents } from './nodes'
// props were passed from the slot using `v-bind="customNodeProps"`
const props = defineProps<NodeProps<CustomData, CustomEvents>>()
console.log(props.data.hello) // 'world'
</script>
<template>
<div>
<Handle type="target" :position="Position.Top" />
<div>{{ label }}</div>
<Handle type="source" :position="Position.Bottom" />
</div>
</template>Vue Flow 将尝试将此节点类型解析为一个组件。优先使用 state 的 nodeTypes 对象中的定义。接着,它会尝试将组件与同名的全局注册组件匹配。最后,它会搜索提供的模板插槽以填充该节点类型。
🌐 Vue Flow will then attempt to resolve this node-type to a component. Priority is given to a definition in the nodeTypes object of the state. Next, it tries to match the component to a globally registered one with the same name. Finally, it searches for a provided template slot to fill in the node-type.
如果在解析组件时没有任何方法产生结果,则使用默认节点类型作为后备。
🌐 If no methods produce a result in resolving the component, the default node-type is used as a fallback.
模板插槽
🌐 Template slots
定义自定义节点最简单的方法之一是将它们作为模板插槽传递。对插槽名称的动态解析会针对你定义的节点类型进行,这意味着类型为 custom 的节点预计会有一个名为 #node-custom 的插槽。
🌐 One of the easiest ways to define custom nodes is, by passing them as template slots. Dynamic resolution to slot-names is done for your user-defined node-types, meaning a node with the type custom is expected to have a slot named #node-custom.
vue
<script setup>
import { VueFlow } from '@vue-flow/core'
import CustomNode from './CustomNode.vue'
const nodes = ref([
{
id: '1',
data: { label: 'Node 1' },
type: 'custom',
position: { x: 50, y: 50 },
}
])
</script>
<template>
<VueFlow :nodes="nodes">
<!-- the expected slot name is `node-custom` -->
<template #node-custom="props">
<CustomNode v-bind="props" />
</template>
</VueFlow>
</template>节点类型对象
🌐 Node-types object
或者,也可以通过将对象作为 prop 传递给 VueFlow 组件(或作为可组合项的选项)来定义节点类型。
🌐 Alternatively, node-types can also be defined by passing an object as a prop to the VueFlow component (or as an option to the composable).
WARNING
采取预防措施,将你的组件标记为原始(使用 Vue 库中的 mark 函数)以防止它们被转换为响应式对象。否则,Vue 会在控制台显示警告。
vue
<script setup>
import { markRaw } from 'vue'
import CustomNode from './CustomNode.vue'
import SpecialNode from './SpecialNode.vue'
const nodeTypes = {
custom: markRaw(CustomNode),
special: markRaw(SpecialNode),
}
const nodes = ref([
{
id: '1',
data: { label: 'Node 1' },
type: 'custom',
},
{
id: '1',
data: { label: 'Node 1' },
type: 'special',
}
])
</script>
<template>
<VueFlow :nodes="nodes" :nodeTypes="nodeTypes" />
</template>TIP
节点属性
🌐 Node Props
你的自定义节点被封装,以便基本功能如拖动或选择可以正常操作。但你可能希望扩展这些功能或在节点内实现你的业务逻辑,因此你的节点会接收以下属性:
🌐 Your custom nodes are enclosed so that fundamental functions like dragging or selecting operate. But you may wish to expand on these features or implement your business logic inside nodes, thus your nodes receive the following properties:
| 属性名称 | 描述 | 类型 | 可选 |
|---|---|---|---|
| id | 唯一节点ID | string | |
| type | 节点类型 | string | |
| selected | 节点已选择 | boolean | |
| connectable | 节点句柄可以连接吗 | 处理可连接对象 | |
| position | 节点在图上的 x、y(相对)位置 | XY位置 | |
| dimensions | DOM 元素尺寸(宽度,高度) | 尺寸 | |
| label | 节点标签,可以是字符串或 VNode。h('div', props, children) | 字符串 | VNode |
| isValidTargetPos deprecated | 当作为新连接的目标时调用 | ValidConnectionFunc | |
| isValidSourcePos deprecated | 当用作新连接的来源时调用 | ValidConnectionFunc | |
| parent | 父节点ID | string | |
| dragging | 节点当前是否正在拖动 | boolean | |
| resizing | 节点当前是否正在调整大小 | boolean | |
| zIndex | 节点 z 索引 | number | |
| targetPosition | 句柄位置 | 位置 | |
| sourcePosition | 句柄位置 | 位置 | |
| dragHandle | 拖动句柄查询选择器 | string | |
| data | 节点的附加数据 | 任何对象 | |
| events | 节点的上下文和自定义事件 | 节点事件开启 |
节点事件
🌐 Node Events
Vue Flow 提供了两种主要的监听节点事件的方式,要么使用 useVueFlow 将监听器绑定到事件处理器,要么将它们绑定到 <VueFlow> 组件。
🌐 Vue Flow provides two main ways of listening to node events, either by using useVueFlow to bind listeners to the event handlers or by binding them to the <VueFlow> component.
vue
<script setup>
import { ref } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
// useVueFlow provides access to the event handlers
const {
onNodeDragStart,
onNodeDrag,
onNodeDragStop,
onNodeClick,
onNodeDoubleClick,
onNodeContextMenu,
onNodeMouseEnter,
onNodeMouseLeave,
onNodeMouseMove
} = useVueFlow()
const nodes = ref([
{
id: '1',
data: { label: 'Node 1' },
position: { x: 50, y: 50 },
},
])
// bind listeners to the event handlers
onNodeDragStart((event) => {
console.log('Node drag started', event)
})
onNodeDrag((event) => {
console.log('Node dragged', event)
})
onNodeDragStop((event) => {
console.log('Node drag stopped', event)
})
// ... and so on
</script>
<template>
<VueFlow :nodes="nodes" />
</template>vue
<script setup>
import { ref } from 'vue'
import { VueFlow } from '@vue-flow/core'
const nodes = ref([
{
id: '1',
data: { label: 'Node 1' },
position: { x: 50, y: 50 },
},
])
function logEvent(name, data) {
console.log(name, data)
}
</script>
<template>
<!-- bind listeners to the event handlers -->
<VueFlow
:nodes="nodes"
@node-drag-start="logEvent('drag start', $event)"
@node-drag="logEvent('drag', $event)"
@node-drag-stop="logEvent('drag stop', $event)"
@node-click="logEvent('click', $event)"
@node-double-click="logEvent('dblclick', $event)"
@node-contextmenu="logEvent('contextmenu', $event)"
@node-mouse-enter="logEvent('mouseenter', $event)"
@node-mouse-leave="logEvent('mouseleave', $event)"
@node-mouse-move="logEvent('mousemove', $event)"
/>
</template>交互以在浏览器控制台中查看事件
自定义外观
🌐 Customizing Appearance
TIP
要覆盖默认主题的样式,请访问主题部分。
用户定义节点
🌐 User-Defined Nodes
在构建新的节点类型时,你需要为它添加一些特定的样式。用户创建的节点没有任何默认样式,因此需要自定义样式。
🌐 When constructing a new node type, it's necessary for you to add some styling specific to it. User-created nodes don't have any default styles associated and thus need custom styling.
css
.vue-flow__node-custom {
background: #9CA8B3;
color: #fff;
padding: 10px;
}在节点内实现滚动
🌐 Implementing Scrolling within Nodes
有时,一个节点可能包含大量内容,使用户在没有滚动功能的情况下很难查看所有内容。为了在不触发节点的缩放或平移行为的情况下实现这种滚动功能,Vue Flow 提供了 noWheelClassName 属性。
🌐 Sometimes, a node might contain a large amount of content, making it difficult for users to view everything without the aid of a scroll function. To facilitate this scrolling ability without invoking zoom or pan behaviors on the node, Vue Flow provides the noWheelClassName property.
noWheelClassName 属性允许你指定一个类名,当该类名应用到某个节点时,将会禁用该节点上的默认滚动缩放或滚动平移事件。
🌐 The noWheelClassName property allows you to specify a class name that, when applied to a node, will disable the default zoom-on-scroll or pan-on-scroll events on that particular node.
默认情况下,noWheelClassName 是 nowheel。
🌐 By default, the noWheelClassName is nowheel.
vue
<script setup>
import { ref } from 'vue'
const listItems = ref(Array.from({ length: 100 }, (_, i) => i))
</script>
<template>
<div class="custom-node-container">
<ul class="nowheel">
<li v-for="item in listItems" :key="item">Item {{ item }}</li>
</ul>
</div>
</template>防止节点内拖拽行为
🌐 Preventing Drag Behavior withing Nodes
在某些情境下,你可能需要与节点的内容进行交互,而不触发节点本身的拖动操作。当节点包含可交互的元素(如输入框、按钮或滑块)时,这尤其有用,因为你希望用户能够与这些元素进行互动。
🌐 There are certain scenarios where you might need to interact with the contents of a node without triggering a drag action on the node itself. This can be particularly useful when nodes contain interactive elements like input boxes, buttons, or sliders that you want your users to engage with.
为实现这一点,Vue Flow 提供了一个 noDragClassName 属性。该属性允许指定一个类名,当将其应用于节点内的某个元素时,用户与该元素交互时不会触发节点的拖动操作。
🌐 To accomplish this, Vue Flow provides a noDragClassName property. This property allows specification of a class name, which when applied to an element within a node, prevents triggering a drag action on the node when the user interacts with that element.
默认情况下,noDragClassName 设置为 nodrag。
🌐 By default, the noDragClassName is set as nodrag.
vue
<script setup>
import { ref } from 'vue'
const inputValue = ref('')
</script>
<template>
<div class="custom-node-container">
<input class="nodrag" v-model="inputValue" />
</div>
</template>