前言
目前为止,我们已经大致了解了模型是如何被渲染的,但是也许还有一些疑问,比如 Node 和 Edge 到底是如何渲染的,因此,我们现在以 Node 和 Edge 的新增和移除走一遍渲染流程。
Node 的新增
我们通过一个示例来说明,首先创建一个 Graph:
// 此处省略预先准备 container 的步骤
const graph = new Graph({
container,
width: 800,
height: 600,
})
然后,我们创建两个节点并添加到画布:
const node1 = Node.create({
shape: 'rect',
x: 80,
y: 80,
width: 100,
height: 100,
});
const node2 = Node.create({
shape: 'rect',
x: 280,
y: 80,
width: 100,
height: 100,
})
graph.addNodes([node1, node2]);
根据我们前面对源码的分析,我们可以很容易的分析这整个过程:
-
由于我们为节点的属性设置了
shape为rect因此Node.create函数从注册表中寻找注册为rect的节点,此节点在shape/rect.ts中被注册,因此返回了被注册好的节点 -
被注册的节点在调用
graph.addNodes方法时依次经过graph.addNodes->graph.addCell->model.addCell->collection.add。我们发现它最终会调用model上的collection的add方法,我们截取其中的added部分:added.forEach((cell, i) => { const args = { cell, index: localIndex + i, options: localOptions, } this.trigger('added', args) if (!localOptions.dryrun) { cell.notify('added', { ...args }) } })其中的第 7 行
this.trigger('added', args)会触发collection上的added事件,在model的setup中监听了该事件:protected setup() { ... collection.on('added', ({ cell }) => { this.onCellAdded(cell) }) ... }它没有重新触发事件。
我们再看第9行
cell.notify('added', { ...args }),它调用了Cell上的notify方法,如下:export class Cell< Properties extends Cell.Properties = Cell.Properties, > extends Basecoat<Cell.EventArgs> { notify<Key extends keyof Cell.EventArgs>( name: Key, args: Cell.EventArgs[Key], ): this notify(name: Exclude<string, keyof Cell.EventArgs>, args: any): this notify<Key extends keyof Cell.EventArgs>( name: Key, args: Cell.EventArgs[Key], ) { this.trigger(name, args) const model = this.model if (model) { model.notify(`cell:${name}`, args) if (this.isNode()) { model.notify(`node:${name}`, { ...args, node: this }) } else if (this.isEdge()) { model.notify(`edge:${name}`, { ...args, edge: this }) } } return this } }如果
cell上存在model,那么会触发model上的cell:added方法,那么此时cell上是否存在model?答案是存在,我们注意到
model.addCell中调用collection.add方法是,传递的参数是this.prepareCell(cell, options),我们可以发现,在prepareCell方法中,为cell设置了model值,也就是cell上存在model。 -
在上一步中,
model上的cell:added方法被触发,在渲染器中的调度器中,它在初始化时监听了cell:added事件:this.model.on('cell:added', this.onCellAdded, this)在
onCellAdded方法中,调用了renderViews方法,它获取cell对应的视图。当视图不存在时(刚被添加的
cell当然不存在视图),会调用createCellView去创建视图,和之前在调度器那部分一样,在最后会调用视图上的confirmUpdate方法进行实际的渲染。
Node 的移除
继续补充上面的代码,我们移除其中一个节点:
graph.removeNode(node1)
我们观察调用链会发现,它依次经过 graph.removeNode -> model.removeCell -> collection.remove -> collection.removeCells 方法 collection.removeCells 方法如下:
protected removeCells(cells: Cell[], options: Collection.RemoveOptions) {
const removed = []
for (let i = 0; i < cells.length; i += 1) {
const cell = this.get(cells[i])
if (cell == null) {
continue
}
const index = this.cells.indexOf(cell)
this.cells.splice(index, 1)
this.length -= 1
delete this.map[cell.id]
removed.push(cell)
this.unreference(cell)
if (!options.dryrun) {
cell.remove()
}
if (!options.silent) {
this.trigger('removed', { cell, index, options })
if (!options.dryrun) {
cell.notify('removed', { cell, index, options })
}
}
}
return removed
}
我们可以看到,在第22行,它触发了 collection 上的 removed 事件,第25行调用了 Cell 上的 notify 方法,和上面的新增一样,如果 cell 上存在 model,那么会触发 model 上的 cell:removed 方法,那么此时 cell 上是否存在 model?
答案是不存在,我们注意到,在第22行触发了 collection 上的 removed 事件,它在 model 中被监听:
collection.on('removed', (args) => {
const cell = args.cell
this.onCellRemoved(cell, args.options)
// Should trigger remove-event manually after cell was removed.
this.notify('cell:removed', args)
if (cell.isNode()) {
this.notify('node:removed', { ...args, node: cell })
} else if (cell.isEdge()) {
this.notify('edge:removed', { ...args, edge: cell })
}
})
然后交给了 onCellRemoved 方法处理,在此方法的最后,它将 cell.model 设置为了 null,也就是说,cell 上不会触发 model 上的 cell:removed 方法,这也是上面监听 removed 事件的处理函数中增加了触发 cell:removed 事件的语句的原因。
model 上的 cell:removed 方法被触发,在渲染器中的调度器中,它在初始化时监听了 cell:removed 事件:
this.model.on('cell:removed', this.onCellRemoved, this)
在 onCellRemoved 方法中,调用了 removeViews 方法,它向任务队列增加了一个任务,回调函数会调用 removeView, removeView 中会调用视图上的 remove 方法,它会将 Dom 节点移除。
Edge 的新增
我们去掉移除节点的方法,加上新增 Edge 的方法:
graph.addEdge({
source: node1,
target: node2,
})
首先,graph.addEdge 会调用 model.addEdge ,由于传入的是元数据,他会调用 createEdge 创建 Edge,我们深入其中发现使用了 Edge.create,再深入,它从注册表中获取了名为 edge 的图形,它被定义在 x6/src/shape/edge.ts 中。回到 model.addEdge,使用默认定义创建边后,调用了 model.addCell,到这里就跟 Node 的新增一致了,不再赘述。
Edge 的移除
graph.removeEdge 直接调用了 model.removeCell 和移除节点一致,这里也不再赘述。