上一章:antv-x6 源码解析(1) - x6 核心包解析
markup
markup
是视图的一部分,指定了渲染时使用的 SVG/HTML
片段。
定义
Markup
的定义如下:
ts
export type Markup = string | Markup.JSONMarkup | Markup.JSONMarkup[]
export namespace Markup {
export interface JSONMarkup {
/**
* The namespace URI of the element. It defaults to SVG namespace
* `"http://www.w3.org/2000/svg"`.
*/
ns?: string | null
/**
* The type of element to be created.
*/
tagName: string
/**
* A unique selector for targeting the element within the `attr`
* cell attribute.
*/
selector?: string | null
/**
* A selector for targeting multiple elements within the `attr`
* cell attribute. The group selector name must not be the same
* as an existing selector name.
*/
groupSelector?: string | string[] | null
attrs?: Attr.SimpleAttrs
style?: Record<string, string | number>
className?: string | string[]
children?: JSONMarkup[]
textContent?: string
}
}
markup
在渲染前会被 parseJSONMarkup
解析为 ParseResult
, 它的定义如下:
ts
export interface ParseResult {
fragment: DocumentFragment
selectors: Selectors
groups: KeyValue<Element[]>
}
GraphView
我们从 GraphView
的 markup
入手来理解这两者间的关系,我们首先看它的定义:
ts
const prefixCls = `${Config.prefixCls}-graph` // `Config.prefixCls` 默认值是 `x6`,即它的值是 `x6-graph`
export const markup: Markup.JSONMarkup[] = [
{
ns: Dom.ns.xhtml, // `http://www.w3.org/1999/xhtml`
tagName: 'div',
selector: 'background',
className: `${prefixCls}-background`,
},
{
ns: Dom.ns.xhtml,
tagName: 'div',
selector: 'grid',
className: `${prefixCls}-grid`,
},
{
ns: Dom.ns.svg, // http://www.w3.org/2000/svg
tagName: 'svg',
selector: 'svg',
className: `${prefixCls}-svg`,
attrs: {
width: '100%',
height: '100%',
'xmlns:xlink': Dom.ns.xlink, // http://www.w3.org/1999/xlink
},
children: [
{
tagName: 'defs',
selector: 'defs',
},
{
tagName: 'g',
selector: 'viewport',
className: `${prefixCls}-svg-viewport`,
children: [
{
tagName: 'g',
selector: 'primer',
className: `${prefixCls}-svg-primer`,
},
{
tagName: 'g',
selector: 'stage',
className: `${prefixCls}-svg-stage`,
},
{
tagName: 'g',
selector: 'decorator',
className: `${prefixCls}-svg-decorator`,
},
{
tagName: 'g',
selector: 'overlay',
className: `${prefixCls}-svg-overlay`,
},
],
},
],
},
]
它的渲染结果如下:
对于其中的 selector
字段,它所在的元素会被加入映射表 selectors
中,其 key 就是 selector
的值。
我们现在重新看 GraphView
的实现,如下:
ts
export class GraphView extends View {
public readonly container: HTMLElement
public readonly background: HTMLDivElement
public readonly grid: HTMLDivElement
public readonly svg: SVGSVGElement
public readonly defs: SVGDefsElement
public readonly viewport: SVGGElement
public readonly primer: SVGGElement
public readonly stage: SVGGElement
public readonly decorator: SVGGElement
public readonly overlay: SVGGElement
constructor(protected readonly graph: Graph) {
super()
const { selectors, fragment } = Markup.parseJSONMarkup(GraphView.markup)
this.background = selectors.background as HTMLDivElement
this.grid = selectors.grid as HTMLDivElement
this.svg = selectors.svg as SVGSVGElement
this.defs = selectors.defs as SVGDefsElement
this.viewport = selectors.viewport as SVGGElement
this.primer = selectors.primer as SVGGElement
this.stage = selectors.stage as SVGGElement
this.decorator = selectors.decorator as SVGGElement
this.overlay = selectors.overlay as SVGGElement
this.container = this.options.container
this.restore = GraphView.snapshoot(this.container)
Dom.addClass(this.container, this.prefixClassName('graph'))
Dom.append(this.container, fragment)
this.delegateEvents()
}
}
GraphView.markup
被解析后生成了 selectors
,它存储的是元素的映射表,GraphView
将其一一取出,作为自身的属性,这使得我们可以通过属性来直接访问它的 Dom 元素。
NodeView
我们再来看 NodeView
的 markup
是如何渲染的,NodeView
的 markup
在 renderMarkup
中渲染,如下:
ts
protected renderMarkup() {
const markup = this.cell.markup
if (markup) {
if (typeof markup === 'string') {
throw new TypeError('Not support string markup.')
}
return this.renderJSONMarkup(markup)
}
throw new TypeError('Invalid node markup.')
}
protected renderJSONMarkup(markup: Markup.JSONMarkup | Markup.JSONMarkup[]) {
const ret = this.parseJSONMarkup(markup, this.container)
this.selectors = ret.selectors
this.container.appendChild(ret.fragment)
}
它从 cell
中获取 markup
, 然后将其解析为 selectors
和 fragment
,最后 fragment
被添加到 container
。
EdgeView
的 markup
的渲染与 NodeView
一致,此处不再赘述。